Java併發控制(synchronized、ReentrantLock、CountDownLatch)
參考資料:
https://blog.csdn.net/zcl_love_wx/article/details/93977947
https://zhuanlan.zhihu.com/p/420560153
Java中鎖的實現方式有兩種:synchronized關鍵字和併發包中的鎖類。synchronized這個同步關鍵字以前效能不是太理想,在隨著不停的優化後,它已經成了同步的首選。
鎖涉及的幾個重要概念:重入鎖、自旋鎖、鎖偏向、鎖粗化、公平鎖與非公平鎖、輕量級鎖與重量級鎖、讀寫鎖與獨佔鎖(樂觀鎖與悲觀鎖)
synchronized、ReentrantLock 都屬於可重入鎖。
synchronized、ReentrantLock 都是悲觀鎖。
synchronized,ReentrantLock 都是獨佔鎖。
synchronized 是重量鎖。
synchronized 是非公平鎖,ReentrantLock可以通過建構函式指定該鎖是公平的還是非公平的,預設是非公平的。
問題1: 可重入鎖如果加了兩把,但是隻釋放了一把會出現什麼問題?
答:程式卡死,執行緒不能出來,也就是說我們申請了幾把鎖,就需要釋放幾把鎖。
問題2: 如果只加了一把鎖,釋放兩次會出現什麼問題?
答:會報錯,java.lang.IllegalMonitorStateException。
CountDownLatch 與 CyclicBarrier
CountDownLatch 它是一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。
CyclicBarrier 也是一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)。
類鎖和物件鎖(重要)
Class A {
// ==>物件鎖:普通例項方法默認同步監視器就是this,
// 即呼叫該方法的物件
public synchronized methodA() {
}
public methodB() {
// ==>物件鎖:this表示是物件鎖
synchronized(this){
}
}
// ==>類鎖:修飾靜態方法
public static synchronized methodC() {
}
public methodD(){
// ==>類鎖:A.class說明是類鎖
synchronized(A.class){}
}
// 普通方法:任何情況下呼叫時,都不會發生競爭
public common(){
}
}
類鎖的5種形式:
Class A {
// 普通字串屬性
private String val;
// 靜態屬性
private static Object staticObj;
// ==>類鎖情況1:synchronized修飾靜態方法
public static synchronized methodA() {
}
public methodB(){
// ==>類鎖情況2:同步塊裡的物件是類
synchronized(A.class){}
}
public methodC(){
// ==>類鎖情況3:同步塊裡的物件是字串
synchronized("ABC"){}
}
public methodD(){
// ==>類鎖情況4:同步塊裡的物件是靜態屬性
synchronized(staticObj){}
}
public methodE(){
// ==>類鎖情況5:同步塊裡的物件是字串屬性
synchronized(val){}
}
}
鎖粗化也提醒了我們平時寫程式碼時,儘量不要在迴圈內使用鎖:
// 粗化前
for(int i=0;i<10000;i++){
// 這會導致頻繁同步程式碼,無謂的消耗系統資源
synchronized(monitor){
doSomething...
}
}
// 粗化後
synchronized(monitor){
for(int i=0;i<10000;i++){
doSomething...
}
}
鎖偏向
偏向鎖指的是,當第一個執行緒請求時,會判斷鎖的物件頭裡的ThreadId欄位的值,如果為空,則讓該執行緒持有偏向鎖,並將ThreadId的值置為當前執行緒ID。當前執行緒再次進入時,如果執行緒ID與ThreadId的值相等,則該執行緒就不會再重複獲取鎖了。因為鎖的請求與釋放是要消耗系統資源的。
如果有其他執行緒也來請求該鎖,則偏向鎖就會撤銷,然後升級為輕量級鎖。如果鎖的競爭十分激烈,則輕量級鎖又會升級為重量級鎖。