java线程:互斥锁与读写锁
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
兩種互斥鎖機(jī)制:
1、synchronized
2、ReentrantLock
ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替換synchronized傳統(tǒng)的鎖機(jī)制,而且采用ReentrantLock的方式更加面向?qū)ο?#xff0c;也更加靈活,網(wǎng)上有很多關(guān)于對(duì)比兩者鎖方式的文章,這里就不多口舌了,大家baidu、google一下就水落石出了。在本博客中也寫關(guān)于這兩種鎖方式實(shí)現(xiàn)的經(jīng)典例子《生產(chǎn)者消費(fèi)者》。
synchronized方式:《java線程:三種方式實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問題_1》
ReentranLock方式:《java線程:三種方式實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問題_2》
?
關(guān)于讀寫鎖,用語言解釋不如直接用代碼詮釋,以下通過兩個(gè)例子講述讀寫鎖以及讀寫鎖的使用:
例子1:
import java.util.HashMap; ?
import java.util.Map; ?
import java.util.concurrent.locks.ReadWriteLock; ?
import java.util.concurrent.locks.ReentrantReadWriteLock; ?
??
/**?
?* @author amber2012?
?* ?
?* 讀寫鎖:ReadWriteLock?
?* ?
?* 在多線程的環(huán)境下,對(duì)同一份數(shù)據(jù)進(jìn)行讀寫,會(huì)涉及到線程安全的問題。比如在一個(gè)線程讀取數(shù)據(jù)的時(shí)候,另外一個(gè)線程在?
?* 寫數(shù)據(jù),而導(dǎo)致前后數(shù)據(jù)的不一致性;一個(gè)線程在寫數(shù)據(jù)的時(shí)候,另一個(gè)線程也在寫,同樣也會(huì)導(dǎo)致線程前后看到的數(shù)據(jù)的?
?* 不一致性。?
?* ?
?* 這時(shí)候可以在讀寫方法中加入互斥鎖,任何時(shí)候只能允許一個(gè)線程的一個(gè)讀或?qū)懖僮?#xff0c;而不允許其他線程的讀或?qū)懖僮?#xff0c;這?
?* 樣是可以解決這樣以上的問題,但是效率卻大打折扣了。因?yàn)樵谡鎸?shí)的業(yè)務(wù)場(chǎng)景中,一份數(shù)據(jù),讀取數(shù)據(jù)的操作次數(shù)通常高?
?* 于寫入數(shù)據(jù)的操作,而線程與線程間的讀讀操作是不涉及到線程安全的問題,沒有必要加入互斥鎖,只要在讀-寫,寫-寫期?
?* 間上鎖就行了。?
?* ?
?* 對(duì)于這種情況,讀寫鎖則最好的解決方案!?
?* ?
?* 讀寫鎖的機(jī)制:?
?* ? ? ?"讀-讀"不互斥?
?* ? ? ?"讀-寫"互斥?
?* ? ? ?"寫-寫"互斥?
?* ?
?* 即在任何時(shí)候必須保證:?
?* ? ? ?只有一個(gè)線程在寫入;?
?* ? ? ?線程正在讀取的時(shí)候,寫入操作等待;?
?* ? ? ?線程正在寫入的時(shí)候,其他線程的寫入操作和讀取操作都要等待;?
?* ?
?* 以下是一個(gè)緩存類:用于演示讀寫鎖的操作:重入、降級(jí)?
?*/ ?
public class CachedData { ?
? ? // 緩存都應(yīng)該是單例的,在這里用單例模式設(shè)計(jì): ?
? ? private static CachedData cachedData = new CachedData(); ?
? ? private final ReadWriteLock lock = new ReentrantReadWriteLock();//讀寫鎖 ?
? ? private Map<String, Object> cache = new HashMap<String, Object>();//緩存 ?
? ? ??
? ? private CachedData(){ ?
? ? } ?
? ? ??
? ? public static CachedData getInstance(){ ?
? ? ? ? return cachedData; ?
? ? } ?
? ? ??
? ? // 讀取緩存: ?
? ? public Object read(String key) { ?
? ? ? ? lock.readLock().lock(); ?
? ? ? ? Object obj = null; ?
? ? ? ? try { ?
? ? ? ? ? ? obj = cache.get(key); ?
? ? ? ? ? ? if (obj == null) { ?
? ? ? ? ? ? ? ? lock.readLock().unlock(); ?
? ? ? ? ? ? ? ? // 在這里的時(shí)候,其他的線程有可能獲取到鎖 ?
? ? ? ? ? ? ? ? lock.writeLock().lock(); ?
? ? ? ? ? ? ? ? try { ?
????????????????????obj = cache.get(key); //這個(gè)是必要的!!!!
? ? ? ? ? ? ? ? ? ? if (obj == null) { ?
? ? ? ? ? ? ? ? ? ? ? ? obj = "查找數(shù)據(jù)庫(kù)"; // 實(shí)際動(dòng)作是查找數(shù)據(jù)庫(kù) ?
? ? ? ? ? ? ? ? ? ? ? ? // 把數(shù)據(jù)更新到緩存中: ?
? ? ? ? ? ? ? ? ? ? ? ? cache.put(key, obj); ?
? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? } finally { ?
? ? ? ? ? ? ? ? ? ? // 當(dāng)前線程在獲取到寫鎖的過程中,可以獲取到讀鎖,這叫鎖的重入,然后導(dǎo)致了寫鎖的降級(jí),稱為降級(jí)鎖。 ?
? ? ? ? ? ? ? ? ? ? // 利用重入可以將寫鎖降級(jí),但只能在當(dāng)前線程保持的所有寫入鎖都已經(jīng)釋放后,才允許重入 reader使用 ?
? ? ? ? ? ? ? ? ? ? // 它們。所以在重入的過程中,其他的線程不會(huì)有獲取到鎖的機(jī)會(huì)(這樣做的好處)。試想,先釋放寫鎖,在 ?
? ? ? ? ? ? ? ? ? ? // 上讀鎖,這樣做有什么弊端?--如果這樣做,那么在釋放寫鎖后,在得到讀鎖前,有可能被其他線程打斷。 ?
? ? ? ? ? ? ? ? ? ? // 重入————>降級(jí)鎖的步驟:先獲取寫入鎖,然后獲取讀取鎖,最后釋放寫入鎖(重點(diǎn)) ?
? ? ? ? ? ? ? ? ? ? lock.readLock().lock(); ??
? ? ? ? ? ? ? ? ? ? lock.writeLock().unlock(); ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? } ?
? ? ? ? } finally { ?
? ? ? ? ? ? lock.readLock().unlock(); ?
? ? ? ? } ?
? ? ? ? return obj; ?
? ? } ?
} ?
?
例子2:
import java.util.Map; ?
import java.util.TreeMap; ?
import java.util.concurrent.locks.Lock; ?
import java.util.concurrent.locks.ReadWriteLock; ?
import java.util.concurrent.locks.ReentrantReadWriteLock; ?
??
import javax.xml.crypto.Data; ?
??
/**?
?* @author amber2012?
?* ?
?* jdk文檔中關(guān)于ReentrantReadWriteLock類使用的一個(gè)很好的例子,以下是具體的介紹:?
?* ?
?* 在使用某些種類的 Collection 時(shí),可以使用 ReentrantReadWriteLock 來提高并發(fā)性。通常,在預(yù)期 collection?
?* 很大,讀取者線程訪問它的次數(shù)多于寫入者線程,并且 entail 操作的開銷高于同步開銷時(shí),這很值得一試。例如,以下?
?* 是一個(gè)使用 TreeMap 的類,預(yù)期它很大,并且能被同時(shí)訪問。 ?
?*/ ?
public class RWDictionary { ?
??
? ? private final Map<String, Data> map = new TreeMap<String, Data>(); ?
? ? private final ReadWriteLock rwl = new ReentrantReadWriteLock(); ?
? ? private final Lock readLock = rwl.readLock(); ?
? ? private final Lock writeLock = rwl.writeLock(); ?
??
? ? public Data get(String key) { ?
? ? ? ? readLock.lock(); ?
? ? ? ? try { ?
? ? ? ? ? ? return map.get(key); ?
? ? ? ? } finally { ?
? ? ? ? ? ? readLock.unlock(); ?
? ? ? ? } ?
? ? } ?
??
? ? public String[] allKeys() { ?
? ? ? ? readLock.lock(); ?
? ? ? ? try { ?
? ? ? ? ? ? return (String[]) map.keySet().toArray(); ?
? ? ? ? } finally { ?
? ? ? ? ? ? readLock.unlock(); ?
? ? ? ? } ?
? ? } ?
??
? ? public Data put(String key, Data value) { ?
? ? ? ? writeLock.lock(); ?
? ? ? ? try { ?
? ? ? ? ? ? return map.put(key, value); ?
? ? ? ? } finally { ?
? ? ? ? ? ? writeLock.unlock(); ?
? ? ? ? } ?
? ? } ?
??
? ? public void clear() { ?
? ? ? ? writeLock.lock(); ?
? ? ? ? try { ?
? ? ? ? ? ? map.clear(); ?
? ? ? ? } finally { ?
? ? ? ? ? ? writeLock.unlock(); ?
? ? ? ? } ?
? ? } ?
} ?
轉(zhuǎn)載于:https://my.oschina.net/v512345/blog/741472
總結(jié)
以上是生活随笔為你收集整理的java线程:互斥锁与读写锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ceph之throttle(io限流)
- 下一篇: HTML5 ArrayBufferVi