適用場(chǎng)景
讀操作頻發(fā),寫(xiě)操作不頻繁。
兩個(gè)線程同時(shí)讀取同一個(gè)共享資源沒(méi)有任何問(wèn)題
如果一個(gè)線程對(duì)共享資源進(jìn)行寫(xiě)操作,此時(shí)就不能有其他線程對(duì)共享資源進(jìn)行讀寫(xiě)
條件分析寫(xiě)操作的優(yōu)先級(jí)高于讀操作,在讀操作頻繁的場(chǎng)景下,如果寫(xiě)操作沒(méi)有高于讀操作的優(yōu)先級(jí),就會(huì)導(dǎo)致寫(xiě)操作線程“餓死”的情況發(fā)生
讀操作觸發(fā)條件:
沒(méi)有線程正在執(zhí)行寫(xiě)操作
沒(méi)有線程在等待執(zhí)行寫(xiě)操作
寫(xiě)操作觸發(fā)條件:沒(méi)有線程正在執(zhí)行讀寫(xiě)操作
代碼實(shí)現(xiàn)
public class ReadWriteLock { ??private int readers = 0; ??private int writers = 0; ??private int writeRequests = 0; ??public synchronized void lockRead() throws InterruptedException { ????while (writers > 0 || writeRequests > 0) { ??????wait(); ????} ????readers++; ??} ??public synchronized void unlockRead() { ????readers--; ????notifyAll(); ??} ??public synchronized void lockWrite() throws InterruptedException { ????writeRequests++; ????while (readers > 0 || writers > 0) { ??????wait(); ????} ????writeRequests--; ????writers++; ??} ??public synchronized void unlockWrite() throws InterruptedException { ????writers--; ????notifyAll(); ??} } |
ReadWriteLockl類中通過(guò)讀鎖、寫(xiě)鎖以兩個(gè)鎖的狀態(tài)控制線程的讀、寫(xiě)操作: writers表示當(dāng)前正在使用寫(xiě)鎖的線程數(shù)量; writeRequests表示等待請(qǐng)求寫(xiě)鎖的線程數(shù)量; readers表示請(qǐng)求讀鎖的線程數(shù)量; 說(shuō)明: 1.線程在獲取讀鎖的時(shí)候,只要沒(méi)有線程擁有寫(xiě)鎖即writers==0同時(shí)沒(méi)有線程請(qǐng)求寫(xiě)鎖即writerRquests==0,那么線程就能成功獲取讀鎖; 2.當(dāng)一個(gè)線程想獲取寫(xiě)鎖的時(shí)候,會(huì)把寫(xiě)鎖的請(qǐng)求數(shù)加1即writeRequests++,然后再嘗試獲取能否獲取寫(xiě)鎖,如果當(dāng)前沒(méi)有線程占用寫(xiě)鎖即writers==0,那么此時(shí)就能成功獲取寫(xiě)鎖,同時(shí)writers++;如果wirters>0表示寫(xiě)鎖此時(shí)被其他線程占用那么當(dāng)前線程會(huì)被阻塞等待寫(xiě)鎖釋放時(shí)被喚醒。 3.寫(xiě)操作的優(yōu)先級(jí)高于讀操作的優(yōu)先級(jí)體現(xiàn)在,線程請(qǐng)求讀鎖時(shí)會(huì)判斷持有寫(xiě)鎖的線程數(shù)和請(qǐng)求寫(xiě)鎖的線程數(shù),即while(writers > 0 || writeRequests > 0){wait();},而線程請(qǐng)求寫(xiě)鎖時(shí)只需要判斷持有寫(xiě)鎖和讀鎖的線程數(shù)即可,即while(readers > 0 || writers > 0) {wait();} |
鎖重入,是指同一線程 外層函數(shù)獲得鎖之后 ,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響。ReentrantLock 和synchronized 都是可重入鎖,可重入鎖最大的作用是避免死鎖。
以自旋鎖為例,如果自旋鎖不是可重入鎖的話,如果一個(gè)線程在第一次獲取鎖執(zhí)行同步代碼前提下,第二次再執(zhí)行同步代碼就產(chǎn)生了死鎖。
以前面的代碼為例:
此時(shí)有兩個(gè)線程Thread1,Thread2
Thread2在Thread1獲取讀鎖以后請(qǐng)求寫(xiě)鎖,readers=1、writers=0、writeRequests=1
若此時(shí)Thread1再次嘗試獲取同一個(gè)讀鎖,根據(jù)已有的代碼writers > 0 || writeRequests > 0,因?yàn)門(mén)hread請(qǐng)求寫(xiě)鎖的原因?qū)е略摋l件成立,Thread1進(jìn)入阻塞狀態(tài),死鎖出現(xiàn)
讀鎖重入public class ReadWriteLock{ ?private Map ?private int writers = 0; ?private int writeRequests = 0; ?public synchronized void lockRead() throws InterruptedException{ ???Thread callingThread = Thread.currentThread(); ???while(! canGrantReadAccess(callingThread)){ ?????wait(); ???} ???readingThreads.put(callingThread, (getAccessCount(callingThread) + 1)); ?} ?public synchronized void unlockRead(){ ???Thread callingThread = Thread.currentThread(); ???int accessCount = getAccessCount(callingThread); ???if(accessCount == 1) { ????readingThreads.remove(callingThread); ???} else { ????readingThreads.put(callingThread, (accessCount -1)); ???} ???notifyAll(); ?} ?private boolean canGrantReadAccess(Thread callingThread){ ???if(writers > 0) return false; ???if(isReader(callingThread) return true; ???if(writeRequests > 0) return false; ???return true; ?} ?private int getReadAccessCount(Thread callingThread){ ???Integer accessCount = readingThreads.get(callingThread); ???if(accessCount == null) return 0; ???return accessCount.intValue(); } ?private boolean isReader(Thread callingThread){ ???return readingThreads.get(callingThread) != null; ?} } |
讀鎖的可重入有兩種情況: 1.當(dāng)前程序中沒(méi)有線程請(qǐng)求寫(xiě)鎖(這種情況是幾乎不存在) 2.當(dāng)前程序中有線程請(qǐng)求寫(xiě)鎖也有線程請(qǐng)求讀鎖,并且有線程已經(jīng)得到了讀鎖 第二種情況是最常見(jiàn)的,因此我們需要知道哪些線程是持有讀鎖的 因此在代碼中使用Map來(lái)存儲(chǔ)已經(jīng)持有讀鎖的線程和對(duì)應(yīng)線程獲取讀鎖的次數(shù),通過(guò)Map就可以判斷對(duì)應(yīng)的線程是否持有讀鎖,調(diào)整之后的代碼在原有判斷"writeRequests >0"和"writers > 0"還加上了判斷當(dāng)前線程是否持有讀鎖的判斷邏輯 |
public class ReadWriteLock{ ?private Map ?private int writeAccesses = 0; ?private int writeRequests = 0; ?private Thread writingThread = null; ?public synchronized void lockWrite() throws InterruptedException{ ???writeRequests++; ???Thread callingThread = Thread.currentThread(); ???while(!canGrantWriteAccess(callingThread)){ ????wait(); ???} ???writeRequests--; ???writeAccesses++; ???writingThread = callingThread; ?} ?public synchronized void unlockWrite() throws InterruptedException{ ???writeAccesses--; ???if(writeAccesses == 0){ ?????writingThread = null; ???} ???notifyAll(); ?} ?private boolean canGrantWriteAccess(Thread callingThread){ ???if(hasReaders()) return false; ???if(writingThread == null) return true; ???if(!isWriter(callingThread)) return false; ???return true; ?} ?private boolean hasReaders(){ ???return readingThreads.size() > 0; ?} ?private boolean isWriter(Thread callingThread){ ???return writingThread == callingThread; ?} } |
寫(xiě)鎖重入,是在當(dāng)前程序里有且只有一個(gè)線程持有寫(xiě)鎖,如果寫(xiě)鎖重入,說(shuō)明當(dāng)前程序中沒(méi)有線程持有讀鎖,寫(xiě)鎖重入只有持有寫(xiě)鎖的線程才能重入,其他的線程就需要進(jìn)入阻塞狀態(tài) |
public class ReadWriteLock{ private MapreadingThreads = new HashMap (); private int writeAccesses = 0; private int writeRequests = 0; private Thread writingThread = null; public synchronized void lockRead() throws InterruptedException{ Thread callingThread = Thread.currentThread(); while(! canGrantReadAccess(callingThread)){ wait(); } readingThreads.put(callingThread,(getReadAccessCount(callingThread) + 1)); } private boolean canGrantReadAccess(Thread callingThread){ #寫(xiě)鎖降級(jí)到讀鎖的邏輯判斷 if(isWriter(callingThread)) return true; if(hasWriter()) return false; if(isReader(callingThread)) return true; if(hasWriteRequests()) return false; return true; } public synchronized void unlockRead(){ Thread callingThread = Thread.currentThread(); if(!isReader(callingThread)){ throw new IllegalMonitorStateException( "Calling Thread does not" + " hold a read lock on this ReadWriteLock"); } int accessCount = getReadAccessCount(callingThread); if(accessCount == 1){ readingThreads.remove(callingThread); } else { readingThreads.put(callingThread, (accessCount -1)); } notifyAll(); } public synchronized void lockWrite() throws InterruptedException{ writeRequests++; Thread callingThread = Thread.currentThread(); while(!canGrantWriteAccess(callingThread)){ wait(); } writeRequests--; writeAccesses++; writingThread = callingThread; } public synchronized void unlockWrite() throws InterruptedException{ if(!isWriter(Thread.currentThread()){ throw new IllegalMonitorStateException( "Calling Thread does not" + " hold the write lock on this ReadWriteLock"); } writeAccesses--; if(writeAccesses == 0){ writingThread = null; } notifyAll(); } private boolean canGrantWriteAccess(Thread callingThread){ #讀鎖轉(zhuǎn)換成寫(xiě)鎖的邏輯判斷 if(isOnlyReader(callingThread)) return true; if(hasReaders()) return false; if(writingThread == null) return true; if(!isWriter(callingThread)) return false; return true; } private int getReadAccessCount(Thread callingThread){ Integer accessCount = readingThreads.get(callingThread); if(accessCount == null) return 0; return accessCount.intValue(); } private boolean hasReaders(){ return readingThreads.size() > 0; } private boolean isReader(Thread callingThread){ return readingThreads.get(callingThread) != null; } private boolean isOnlyReader(Thread callingThread){ return readingThreads.size() == 1 && readingThreads.get(callingThread) != null; } private boolean hasWriter(){ return writingThread != null; } private boolean isWriter(Thread callingThread){ return writingThread == callingThread; } private boolean hasWriteRequests(){ return this.writeRequests > 0; } }
參考文獻(xiàn)
http://ifeve.com/read-write-l...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71556.html
摘要:語(yǔ)言在之前,提供的唯一的并發(fā)原語(yǔ)就是管程,而且之后提供的并發(fā)包,也是以管程技術(shù)為基礎(chǔ)的。但是管程更容易使用,所以選擇了管程。線程進(jìn)入條件變量的等待隊(duì)列后,是允許其他線程進(jìn)入管程的。并發(fā)編程里兩大核心問(wèn)題互斥和同步,都可以由管程來(lái)幫你解決。 并發(fā)編程這個(gè)技術(shù)領(lǐng)域已經(jīng)發(fā)展了半個(gè)世紀(jì)了。有沒(méi)有一種核心技術(shù)可以很方便地解決我們的并發(fā)問(wèn)題呢?這個(gè)問(wèn)題, 我會(huì)選擇 Monitor(管程)技術(shù)。Ja...
摘要:此時(shí)線程和會(huì)再有一個(gè)線程能夠獲取寫(xiě)鎖,假設(shè)是,如果不采用再次驗(yàn)證的方式,此時(shí)會(huì)再次查詢數(shù)據(jù)庫(kù)。而實(shí)際上線程已經(jīng)把緩存的值設(shè)置好了,完全沒(méi)有必要再次查詢數(shù)據(jù)庫(kù)。 大家知道了Java中使用管程同步原語(yǔ),理論上可以解決所有的并發(fā)問(wèn)題。那 Java SDK 并發(fā)包里為什么還有很多其他的工具類呢?原因很簡(jiǎn)單:分場(chǎng)景優(yōu)化性能,提升易用性 今天我們就介紹一種非常普遍的并發(fā)場(chǎng)景:讀多寫(xiě)少場(chǎng)景。實(shí)際工作...
摘要:公平鎖非公平鎖公平鎖公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。加鎖后,任何其他試圖再次加鎖的線程會(huì)被阻塞,直到當(dāng)前進(jìn)程解鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。 Java 中15種鎖的介紹 在讀很多并發(fā)文章中,會(huì)提及各種各樣鎖如公平鎖,樂(lè)觀鎖等等,這篇文章介紹各種鎖的分類。介紹的內(nèi)容如下: 公平鎖 / 非公平鎖 可重入鎖 / 不可重入鎖 獨(dú)享鎖 / 共享鎖 互斥鎖 / 讀...
摘要:鎖的使用建議減少鎖持有時(shí)間減少鎖粒度讀寫(xiě)鎖替代獨(dú)占鎖鎖分離鎖粗化減少鎖的持有時(shí)間減少鎖的持有時(shí)間有助于降低沖突的可能性進(jìn)而提升并發(fā)能力減少鎖粒度例如內(nèi)部分為個(gè)加鎖時(shí)不會(huì)像一樣全局加鎖只需要對(duì)相應(yīng)加鎖但是如果需要獲取全局的信息比如首先會(huì)使用無(wú) 鎖的使用建議 減少鎖持有時(shí)間 減少鎖粒度 讀寫(xiě)鎖替代獨(dú)占鎖 鎖分離 鎖粗化 減少鎖的持有時(shí)間 減少鎖的持有時(shí)間有助于降低沖突的可能性,進(jìn)而...
閱讀 1633·2021-09-22 15:52
閱讀 3546·2021-09-22 14:59
閱讀 2972·2021-09-02 15:12
閱讀 1046·2021-08-20 09:35
閱讀 1634·2019-08-30 14:09
閱讀 2764·2019-08-30 13:56
閱讀 1732·2019-08-26 18:27
閱讀 3423·2019-08-26 13:37