Java concurrency之公平鎖(二)_動力節(jié)點Java學院整理
釋放公平鎖(基于JDK1.7.0_40)
1. unlock()
unlock()在ReentrantLock.java中實現(xiàn)的,源碼如下:
public void unlock() { sync.release(1); }
說明:
unlock()是解鎖函數(shù),它是通過AQS的release()函數(shù)來實現(xiàn)的。
在這里,“1”的含義和“獲取鎖的函數(shù)acquire(1)的含義”一樣,它是設置“釋放鎖的狀態(tài)”的參數(shù)。由于“公平鎖”是可重入的,所以對于同一個線程,每釋放鎖一次,鎖的狀態(tài)-1。
關于AQS, ReentrantLock 和 sync的關系如下:
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { ... } ... }
從中,我們發(fā)現(xiàn):sync是ReentrantLock.java中的成員對象,而Sync是AQS的子類。
2. release()
release()在AQS中實現(xiàn)的,源碼如下:
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
說明:
release()會先調(diào)用tryRelease()來嘗試釋放當前線程鎖持有的鎖。成功的話,則喚醒后繼等待線程,并返回true。否則,直接返回false。
3. tryRelease()
tryRelease()在ReentrantLock.java的Sync類中實現(xiàn),源碼如下:
protected final boolean tryRelease(int releases) { // c是本次釋放鎖之后的狀態(tài) int c = getState() - releases; // 如果“當前線程”不是“鎖的持有者”,則拋出異常! if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; // 如果“鎖”已經(jīng)被當前線程徹底釋放,則設置“鎖”的持有者為null,即鎖是可獲取狀態(tài)。 if (c == 0) { free = true; setExclusiveOwnerThread(null); } // 設置當前線程的鎖的狀態(tài)。 setState(c); return free; }
說明:
tryRelease()的作用是嘗試釋放鎖。
(01) 如果“當前線程”不是“鎖的持有者”,則拋出異常。
(02) 如果“當前線程”在本次釋放鎖操作之后,對鎖的擁有狀態(tài)是0(即,當前線程徹底釋放該“鎖”),則設置“鎖”的持有者為null,即鎖是可獲取狀態(tài)。同時,更新當前線程的鎖的狀態(tài)為0。
getState(), setState()在前一章已經(jīng)介紹過,這里不再說明。
getExclusiveOwnerThread(), setExclusiveOwnerThread()在AQS的父類AbstractOwnableSynchronizer.java中定義,源碼如下:
public abstract class AbstractOwnableSynchronizer implements java.io.Serializable { // “鎖”的持有線程 private transient Thread exclusiveOwnerThread; // 設置“鎖的持有線程”為t protected final void setExclusiveOwnerThread(Thread t) { exclusiveOwnerThread = t; } // 獲取“鎖的持有線程” protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; } ... }
4. unparkSuccessor()
在release()中“當前線程”釋放鎖成功的話,會喚醒當前線程的后繼線程。
根據(jù)CLH隊列的FIFO規(guī)則,“當前線程”(即已經(jīng)獲取鎖的線程)肯定是head;如果CLH隊列非空的話,則喚醒鎖的下一個等待線程。
下面看看unparkSuccessor()的源碼,它在AQS中實現(xiàn)。
private void unparkSuccessor(Node node) { // 獲取當前線程的狀態(tài) int ws = node.waitStatus; // 如果狀態(tài)<0,則設置狀態(tài)=0 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); //獲取當前節(jié)點的“有效的后繼節(jié)點”,無效的話,則通過for循環(huán)進行獲取。 // 這里的有效,是指“后繼節(jié)點對應的線程狀態(tài)<=0” Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } // 喚醒“后繼節(jié)點對應的線程” if (s != null) LockSupport.unpark(s.thread); }
說明:
unparkSuccessor()的作用是“喚醒當前線程的后繼線程”。后繼線程被喚醒之后,就可以獲取該鎖并恢復運行了。
關于node.waitStatus的說明,請參考“上一章關于Node類的介紹”。
總結
“釋放鎖”的過程相對“獲取鎖”的過程比較簡單。釋放鎖時,主要進行的操作,是更新當前線程對應的鎖的狀態(tài)。如果當前線程對鎖已經(jīng)徹底釋放,則設置“鎖”的持有線程為null,設置當前線程的狀態(tài)為空,然后喚醒后繼線程。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Java 中ConcurrentHashMap的實現(xiàn)
本文主要介紹Java 中ConcurrentHashMap的實現(xiàn),這里整理了詳細的資料,及簡單實例代碼,有興趣的小伙伴可以參考下2016-09-09劍指Offer之Java算法習題精講數(shù)組查找與字符串交集
跟著思路走,之后從簡單題入手,反復去看,做過之后可能會忘記,之后再做一次,記不住就反復做,反復尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化2022-03-03Java實現(xiàn)解析dcm醫(yī)學影像文件并提取文件信息的方法示例
這篇文章主要介紹了Java實現(xiàn)解析dcm醫(yī)學影像文件并提取文件信息的方法,結合實例形式分析了java基于第三方庫文件針對dcm醫(yī)學影像文件的解析操作相關實現(xiàn)技巧,需要的朋友可以參考下2018-04-04IntelliJ IDEA中新建Java class的解決方案
今天小編就為大家分享一篇關于IntelliJ IDEA中新建Java class的解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10SpringBoot實現(xiàn)RabbitMQ監(jiān)聽消息的四種方式
本文主要介紹了SpringBoot實現(xiàn)RabbitMQ監(jiān)聽消息的四種方式,包括@RabbitListener,MessageListener接口,MessageListenerAdapter適配器,@RabbitHandler這幾種,感興趣的可以了解一下2024-05-05