JUC是JAVA推出頂替synchronize的jar包
JUC : java.util.concurrent 也就是java併發工具包
裏面含有線程池, 阻塞隊列,同步器,計時器等組件 etc…
OK 以上關於JUC 的定位已經瞭解了,那麼既然JUC 想要代替synchronize關鍵字,那麼關鍵 就在與 lock(鎖)
然而既然實現鎖,那麼有兩點肯定是避不開的
1) 線程如何爭搶鎖?
鎖 一般有兩個特性可以被稱之爲鎖:
1) 共享性
2) 排他性
所以在爭搶鎖的時候,那麼這個鎖一定在在多個線程中共享的, 我們所要做的就是如何保證它在爭搶鎖時的一個安全性.
在java中有一種實現方式被稱之爲CAS 使用的是 樂觀鎖的理念
所謂的樂觀鎖 以java的實現如下:
入參: 對象, 內存值, 預期值, 更新值
其中 對象即爲被多個線程共享的鎖對象
內存值 就是鎖狀態在內存中存在的位置
假設當前 鎖狀態的含義爲:
0 代表鎖是沒有被佔用
1 代表當前鎖已被佔用
那麼在樂觀鎖中 預期值就是 0 ,更新值就是 1
判斷邏輯:
1) 使用預期值 與 內存中的鎖狀態做對比
2) 如果返回true代表當前鎖沒有被佔用,
即修改成 參數中的更新值
3) 如果返回false就算作爭搶失敗
(ps: java中的CAS最後的調用的是 native中的本地函數實現的
在此不做展開)
在線程爭搶鎖失敗之後, 總不能不管線程,讓其跳過當前代碼塊,走下面的流程.
因此我們需要一個線程管理者,
在JUC裏面這個管理者 被稱之爲AQS.
其實現方式爲雙向鏈表,
AQS的工作邏輯如下:
1) 當ThreadA 與ThreadB 互相爭搶鎖
2) ThreadA爭搶鎖成功,將其作爲AQS head節點
3) 將其放入AQS隊列,在放入AQS隊列之前搶一次鎖
4) ThreadB 爭搶鎖失敗,在只有兩個線程的情況下 ThreadB 是作爲 ThreadA 的next 存貯在AQS隊列中
5) 對ThreadB 的node節點 使用自旋,死循環搶鎖
6) 短暫時間片之後,如果沒有搶到鎖將調用 park函數將線程掛起
7) 當ThreadA 執行完畢釋放鎖之後,喚醒 next 節點 ,
在這裏也就是 ThreadB節點
8) ThreadB節點進行鎖的爭搶,如果搶奪成功 則將
ThreadB節點設置爲AQS隊列的head 節點
(ps: 誰搶奪鎖成功誰就是 AQS隊列中的head節點)
由以上粗略的工作邏輯我們可以大致瞭解下 AQS是如何工作的, 也瞭解了在JUC之中 如果線程搶不到鎖, 是被如何分配的
題外話:
在java中 實現lock接口的有 ReentrantLock
實現依賴AQS隊列的有 FairSync(公平鎖) 與 NonfairSync(非公平鎖)