在 jdk 6 之前,儘管 synchronized 關鍵字實現同步很方便,但是這種同步操作很重量級,很大程度上影響程式的執行效率,所以對於開發者來說,使用起來會有點畏懼。但是在 jdk 6 之後,對 synchronized 進行了很大的優化, 引入了偏向鎖,適應性自旋,輕量級鎖和重量級鎖,鎖粗化等手段,目前 synchronized 和 reentrantlock 的效能基本能夠持平,所以一般情況下,還是建議使用 synchronized 關鍵字,會使得程式簡潔和易讀。
jdk 6 之前
[外鏈轉存失敗(img-u9zk8zoy-1566270580439)(
jdk 6 之後
[外鏈轉存失敗(img-p4v6a4pu-1566270580440)(
但是 reentrantlock 也有很多優點,reentrantlock 是 lock 的實現類,是乙個互斥的同步器,在多執行緒高競爭條件下,reentrantlock 比 synchronized 有更加優異的效能表現。
(1) lock 使用起來比較靈活,但是必須有釋放鎖的配合動作
lock 必須手動獲取與釋放鎖,而 synchronized 不需要手動釋放和開啟鎖 lock 只適用於**塊鎖,而 synchronized 可用於修飾方法、**塊等。
(2) reentrantlock的優勢體現在:
具備嘗試非阻塞地獲取鎖的特性:當前執行緒嘗試獲取鎖,如果這一時刻鎖沒有被其他執行緒獲取到,則成功獲取並持有鎖
能被中斷地獲取鎖的特性:與synchronized不同,獲取到鎖的執行緒能夠響應中斷,當獲取到鎖的執行緒被中斷時,中斷異常將會被丟擲,同時鎖會被釋放
超時獲取鎖的特性:在指定的時間範圍內獲取鎖;如果截止時間到了仍然無法獲取鎖,則返回
(3) 在使用 reentrantlock 時,要注意:在 finally 中釋放鎖,目的是保證在獲取鎖之後,最終能夠被釋放
不要將獲取鎖的過程寫在 try 塊內,因為如果在獲取鎖時發生了異常,異常丟擲的同時,也會導致鎖無法被釋放。
(4) reentrantlock 提供了乙個 newcondition 的方法,以便使用者在同一鎖的情況下可以根據不同的情況執行等待或喚醒的動作。
reentrantlock 是 lock 介面的實現類,lock 提供了以下幾個方法:
// 獲取鎖,獲取過程中不可中斷
void
lock()
;// 獲取鎖,獲取過程中可中斷,中斷後丟擲 interruptedexception 異常
void
lockinterruptibly()
throws interruptedexception;
// 非阻塞獲取鎖,無論是否獲取到均返回,true 獲取成功,false 獲取失敗
boolean
trylock()
;// 嘗試獲取鎖,在 time 時間內獲取到返回 true,超時則返回 false
boolean
trylock
(long time, timeunit unit)
throws interruptedexception;
// 釋放鎖,一般在 finally 塊中
void
unlock()
;// 建立等待條件
condition newcondition()
;
2.1 lock 鎖使用
reentrantlock 鎖使用起來也比較簡單,主要根據場景來進行選擇,有一點需要注意,如果不是使用 trylock 的方法,需要在 finally 塊中釋放鎖。下面給出幾種獲取鎖的使用示例**。
public
class
reentrantlocktest
thread.
sleep
(3500);
}catch
(exception e)
finally}}
/** * trylock
*/private
static
class
myrunnable1
implements
runnable
else}}
/** * trylock(long timeout, timeunit unit),超時獲取失敗,中間可以被中斷
*/private
static
class
myrunnable2
implements
runnable
catch
(interruptedexception e)
if(islock)
else}}
/** * lockinterruptibly 中間可以被中斷,如果不中斷就一直嘗試獲取鎖
*/private
static
class
myrunnable3
implements
runnable
catch
(interruptedexception e)}}
private
static
void
execute()
}catch
(exception e)
finally
}public
static
void
main
(string[
] args)
throws interruptedexception
private
static
void
test1()
private
static
void
test2()
private
static
void
test3()
private
static
void
test4()
throws interruptedexception
}
2.2 condition 使用
對應乙個 reentrantlock 鎖,可以有多個條件,這是 synchronized 不具備的靈活性。synchronized 中方法或者塊中,只能使用鎖的 wait() 和 notify()、notifyall()。而 reentrantlock 的乙個鎖可以有多個條件,reentrantlock 的乙個鎖對應乙個同步佇列,多個條件對應多個等待佇列。下面使用 reentrantlock 的條件實現生產者 - 消費者模型。
public
class
producerconsumer1
count++
; system.out.
println
("生產者 ---- > "
+ count)
; thread.
sleep
(500);
sconsumercondition.
signal()
;}catch
(interruptedexception e)
finally}}
}private
static
class
consumer
implements
runnable
count--
; system.out.
println
("消費者 ---- > "
+ count)
; thread.
sleep
(300);
sproducercondition.
signal()
;}catch
(interruptedexception e)
finally}}
}public
static
void
main
(string[
] args)
throws interruptedexception
}
2.3 其他說明
reetrantlock 和 synchronized 都是一種支援重進入的鎖,即該鎖可以支援乙個執行緒對資源重複加鎖。但是 synchronized 不支援公平鎖,reetrantlock 支援公平鎖與非公平鎖。公平與非公平指的是在請求先後順序上,先對鎖進行請求的就一定先獲取到鎖,那麼這就是公平鎖,反之,如果對於鎖的獲取並沒有時間上的先後順序,如後請求的執行緒可能先獲取到鎖,這就是非公平鎖,一般而言非,非公平鎖機制的效率往往會勝過公平鎖的機制,但在某些場景下,可能更注重時間先後順序,那麼公平鎖自然是很好的選擇。需要注意的是 reetrantlock 支援對同一執行緒重加鎖,但是加鎖多少次,就必須解鎖多少次,這樣才可以成功釋放鎖。
golang學習筆記之併發優化(二)
通往管道的資料如果一次打包傳送的效能要高於多次傳送。請看下面裡面,這個裡面我往管道裡面傳送50000000次 func main close done for i 0 i 50000000 i close c done 結果如下 time go run mutilsend.go 1249999975...
Java併發之執行緒池的學習
相較執行緒,執行緒池的好處 1 減少在建立和銷毀執行緒上所花的時間以及系統資源的開銷 2 可以重複利用執行緒池中已建立的執行緒 直接介紹常見的幾種執行緒池 1 newfixedthreadpool int nthreads 建立乙個固定數量執行緒集合的執行緒池。新的執行緒加入後,如果正在執行的執行緒...
Java併發之執行緒池學習筆記
juc包下有乙個重要的執行緒池的實現,大大優化方便了我們對執行緒的使用,而不再是傳統的new乙個thread。執行緒池相對傳統的直接建立執行緒主要有三個優點 1.統一管理執行緒,可以重用存在的執行緒,避免多次的建立 消亡的開銷,使得效能表現得更好 2.可以有效控制最大併發執行緒數,提高系統資源利用率...