午夜剧场伦理_日本一道高清_国产又黄又硬_91黄色网战_女同久久另类69精品国产_妹妹的朋友在线

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java 重入鎖和讀寫(xiě)鎖的具體使用

瀏覽:38日期:2022-08-15 14:38:04
重入鎖

重入鎖 ReentrantLock,顧名思義,就是支持重進(jìn)入的鎖,它表示該鎖能夠支持一個(gè)線(xiàn)程對(duì)資源的重復(fù)加鎖。除此之外,該鎖還支持獲取鎖時(shí)的公平和非公平性選擇

所謂不支持重進(jìn)入,可以考慮如下場(chǎng)景:當(dāng)一個(gè)線(xiàn)程調(diào)用 lock() 方法獲取鎖之后,如果再次調(diào)用 lock() 方法,則該線(xiàn)程將會(huì)被自己阻塞,原因是在調(diào)用 tryAcquire(int acquires) 方法時(shí)會(huì)返回 false,從而導(dǎo)致線(xiàn)程阻塞

synchronize 關(guān)鍵字隱式的支持重進(jìn)入,比如一個(gè) synchronize 修飾的遞歸方法,在方法執(zhí)行時(shí),執(zhí)行線(xiàn)程在獲取鎖之后仍能連續(xù)多次地獲得該鎖。ReentrantLock 雖然不能像 synchronize 關(guān)鍵字一樣支持隱式的重進(jìn)入,但在調(diào)用 lock() 方法時(shí),已經(jīng)獲得鎖的線(xiàn)程,能夠再次調(diào)用 lock() 方法獲取鎖而不被阻塞

1. 實(shí)現(xiàn)重進(jìn)入

重進(jìn)入特性的實(shí)現(xiàn)需要解決以下兩個(gè)問(wèn)題:

線(xiàn)程再次獲取鎖鎖需要去識(shí)別獲取鎖的線(xiàn)程是否為當(dāng)前占據(jù)鎖的線(xiàn)程,如果是,則再次成功獲取

鎖的最終釋放線(xiàn)程重復(fù) n 次獲取鎖,隨后在第 n 次釋放該鎖后,其他線(xiàn)程能獲取到鎖。實(shí)現(xiàn)此功能,理應(yīng)考慮使用計(jì)數(shù)

ReentrantLock 通過(guò)組合自定義同步器來(lái)實(shí)現(xiàn)鎖的獲取與釋放,以非公平鎖實(shí)現(xiàn)為例,獲取同步狀態(tài)的代碼如下所示,主要是增加了再次獲取同步狀態(tài)的處理邏輯

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // 判斷當(dāng)前線(xiàn)程是否為獲取鎖的線(xiàn)程 else if (current == getExclusiveOwnerThread()) { // 將同步值進(jìn)行增加,并返回 true int nextc = c + acquires; if (nextc < 0) throw new Error('Maximum lock count exceeded'); setState(nextc); return true; } return false;}

考慮到成功獲取鎖的線(xiàn)程再次獲取鎖,只是增加同步狀態(tài)值,這也就要求 ReentrantLock 在釋放同步狀態(tài)時(shí)減少同步狀態(tài)值,該方法代碼如下:

protected final boolean tryRelease(int releases) { // 減少狀態(tài)值 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 當(dāng)同步狀態(tài)為0,將占有線(xiàn)程設(shè)為null,并返回true,表示釋放成功 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free;}2. 公平與非公平獲取鎖的區(qū)別

如果一個(gè)鎖是公平的,那么鎖的獲取順序就應(yīng)該符合請(qǐng)求的絕對(duì)時(shí)間順序,也即 FIFO。回顧上一節(jié),非公平鎖只要 CAS 設(shè)置同步狀態(tài)成功,即表示當(dāng)前線(xiàn)程獲取了鎖,而公平鎖則不同,代碼如下:

protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { /* * 唯一不同的就是判斷條件多了 hasQueuedPredecessors() * 該方法用來(lái)判斷當(dāng)前節(jié)點(diǎn)是否有前驅(qū)節(jié)點(diǎn) * 如果該方法返回 true,表示有線(xiàn)程比當(dāng)前線(xiàn)程更早請(qǐng)求獲取鎖 * 因此需要等待前驅(qū)線(xiàn)程釋放鎖之后才能繼續(xù)獲取鎖 */ if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error('Maximum lock count exceeded'); setState(nextc); return true; } return false;}讀寫(xiě)鎖

之前提到的鎖基本都是排它鎖,同一時(shí)刻只允許一個(gè)線(xiàn)程訪(fǎng)問(wèn),而讀寫(xiě)鎖在同一時(shí)刻可以允許多個(gè)線(xiàn)程訪(fǎng)問(wèn),但在寫(xiě)線(xiàn)程訪(fǎng)問(wèn)時(shí),所有的讀線(xiàn)程和其他寫(xiě)線(xiàn)程均被阻塞。讀寫(xiě)鎖維護(hù)了一對(duì)鎖,一個(gè)讀鎖和一個(gè)寫(xiě)鎖,通過(guò)分離讀鎖和寫(xiě)鎖,使得并發(fā)性相比一般的排它鎖有了很大提升

1. 接口示例

下面通過(guò)緩存示例說(shuō)明讀寫(xiě)鎖的使用方式

public class Cache { static Map<String, Object> map = new HashMap<>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock r = rwl.readLock(); static Lock w = rwl.writeLock(); /** * 獲取一個(gè) key 對(duì)應(yīng)的 value */ public static Object get(String key) { r.lock(); try { return map.get(key); } finally { r.unlock(); } } /** * 設(shè)置 key 對(duì)應(yīng)的 value,并返回舊的 value */ public static Object put(String key, Object value) { w.lock(); try { return map.put(key, value); } finally { w.unlock(); } } /** * 清空所有的內(nèi)容 */ public static void clear() { w.lock(); try { map.clear(); } finally { w.unlock(); } }}2. 讀寫(xiě)狀態(tài)的設(shè)計(jì)

讀寫(xiě)鎖同樣依賴(lài)自定義同步器來(lái)實(shí)現(xiàn)功能,而讀寫(xiě)狀態(tài)就是其同步器狀態(tài)。讀寫(xiě)鎖的自定義同步器需要在同步狀態(tài)(一個(gè)整型變量)上維護(hù)多個(gè)讀線(xiàn)程和一個(gè)寫(xiě)線(xiàn)程的狀態(tài),為此需要讀寫(xiě)鎖將變量切分成兩部分,高 16 位表示讀,低 16 位表示寫(xiě)

Java 重入鎖和讀寫(xiě)鎖的具體使用

上圖表示一個(gè)線(xiàn)程已經(jīng)獲取了寫(xiě)鎖,且重進(jìn)入了兩次,同時(shí)也連續(xù)兩次獲取了讀鎖。通過(guò)位運(yùn)算可以迅速確定讀和寫(xiě)各自的狀態(tài),假設(shè)當(dāng)前同步狀態(tài)值為 S,則:

寫(xiě)狀態(tài)等于 S & 0x0000FFFF(將高 16 位全部抹去) 讀狀態(tài)等于 S >>> 16(無(wú)符號(hào)右移 16 位) 當(dāng)寫(xiě)狀態(tài)增加 1 時(shí),等于 S + 1 當(dāng)讀狀態(tài)增加 1 時(shí),等于 S + (1<<6),也就是 S + 0x00010000

根據(jù)狀態(tài)的劃分能得出一個(gè)結(jié)論:S 不等于 0 時(shí),當(dāng)寫(xiě)狀態(tài)(S & 0x0000FFFF)等于 0 時(shí),則讀狀態(tài)(S >>> 16)大于 0,即讀鎖已被獲取

3. 寫(xiě)鎖的獲取與釋放

寫(xiě)鎖是一個(gè)支持重進(jìn)入的排它鎖。如果當(dāng)前線(xiàn)程已經(jīng)獲取了寫(xiě)鎖,則增加寫(xiě)狀態(tài)。如果當(dāng)前線(xiàn)程在獲取寫(xiě)鎖時(shí),讀鎖已被獲取,或者該線(xiàn)程不是獲取寫(xiě)鎖的線(xiàn)程,則當(dāng)前線(xiàn)程進(jìn)入等待狀態(tài),獲取寫(xiě)鎖的代碼如下:

protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread(); int c = getState(); // exclusiveCount 方法會(huì)用 c & 0x0000FFFF,即得出寫(xiě)狀態(tài)個(gè)數(shù) int w = exclusiveCount(c); if (c != 0) { // 根據(jù)上面提到的推論,c 不等于 0,而 w 等于 0,證明存在讀鎖 // 當(dāng)前線(xiàn)程也不是獲取了寫(xiě)鎖的線(xiàn)程 if (w == 0 || current != getExclusiveOwnerThread()) return false; if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error('Maximum lock count exceeded'); setState(c + acquires); return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; setExclusiveOwnerThread(current); return true;}

寫(xiě)鎖的每次釋放均會(huì)減少寫(xiě)狀態(tài),當(dāng)寫(xiě)狀態(tài)為 0 時(shí)表示寫(xiě)鎖已被釋放,從而等待的讀寫(xiě)線(xiàn)程能夠繼續(xù)訪(fǎng)問(wèn)讀寫(xiě)鎖,同時(shí)前次寫(xiě)線(xiàn)程的修改對(duì)后續(xù)讀寫(xiě)線(xiàn)程可見(jiàn)

4. 讀鎖的獲取與釋放

讀鎖是一個(gè)支持重進(jìn)入的共享鎖,它能被多個(gè)線(xiàn)程同時(shí)獲取,在沒(méi)有其他寫(xiě)線(xiàn)程訪(fǎng)問(wèn)時(shí),讀鎖總能被成功獲取,這里對(duì)獲取讀鎖的代碼做了簡(jiǎn)化:

protected final int tryAcquireShared(int unused) { for(;;) { int c = getState(); int nextc = c + (1<<16); if(nextc < c) { throw new Error('Maximum lock count exceeded'); } // 如果其他線(xiàn)程已經(jīng)獲取寫(xiě)鎖,則讀取獲取失敗 if(exclusiveCount(c) != 0 && owner != Thread.currentThread()) { return -1; } if(compareAndSetState(c, nextc)) { return 1; } }}

讀鎖的每次釋放均減少讀狀態(tài),減少的值是 1<<16

5. 鎖降級(jí)

鎖降級(jí)指的是寫(xiě)鎖降級(jí)成為讀鎖。如果當(dāng)前線(xiàn)程擁有寫(xiě)鎖,然后將其釋放,最后再獲取讀鎖,這種分段完成的過(guò)程不能稱(chēng)之為鎖降級(jí)。鎖降級(jí)是指把持住寫(xiě)鎖,再獲取讀鎖,隨后釋放寫(xiě)鎖的過(guò)程

public void processData() { readLock.lock(); if(!update) { // 必須先釋放讀鎖 readLock.unlock(); // 鎖降級(jí)從寫(xiě)鎖獲取到開(kāi)始 writeLock.lock(); try { if(!update) { // 準(zhǔn)備數(shù)據(jù)的流程(略) update = true; } readLock.lock(); } finally { writeLock.unlock(); } } try { // 使用數(shù)據(jù)的流程(略) } finally { readLock.unlock(); }}

上例中,當(dāng)數(shù)據(jù)發(fā)生變更,則 update(使用 volatile 修飾)被設(shè)置為 false,此時(shí)所有訪(fǎng)問(wèn) processData 方法的線(xiàn)程都能感知到變化,但只有一個(gè)線(xiàn)程能獲取到寫(xiě)鎖,其余線(xiàn)程會(huì)被阻塞在寫(xiě)鎖的 lock 方法上。當(dāng)前線(xiàn)程獲取寫(xiě)鎖完成數(shù)據(jù)準(zhǔn)備之后,再次獲取讀鎖,隨后釋放寫(xiě)鎖,完成鎖降級(jí)

到此這篇關(guān)于Java 重入鎖和讀寫(xiě)鎖的具體使用的文章就介紹到這了,更多相關(guān)Java 重入鎖和讀寫(xiě)鎖內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 夜夜夜夜操 | 久久久视频在线 | 日韩免费在线观看视频 | 免费看的黄色 | 亚洲欧洲国产精品 | 4438全国成人免费 | 免费看片91| 超碰在线看 | 九九免费精品视频 | 97中文字幕在线观看 | 国产成人+综合亚洲+天堂 | 久久国产秒 | 久99热| 日韩欧美亚洲一区二区 | 久久午夜国产精品 | 毛片网站在线看 | 国产午夜一区 | 女同一区二区三区 | 一区二区三区四区免费视频 | 一区二区国产精品视频 | 免费在线观看一区二区三区 | 国产婷婷色一区二区三区在线 | 五十路中文字幕 | 亚洲色图av在线 | 日日摸日日添日日躁av | 九九热伊人 | 日韩三区四区 | 天天艹天天爽 | 香蕉福利视频 | 亚洲69av| 国产在线视频91 | 国产精品久久影院 | 日韩中文字幕网 | 国产高清久久久 | 日韩一区二区三区在线播放 | 午夜婷婷网| 永久免费黄色 | 日本一级片在线观看 | 全部免费毛片在线播放一个 | 国产二区三区 | 中国黄色一级视频 |