1、簡單使用Lock鎖java
Java 5中引入了新的鎖機制——java.util.concurrent.locks中的顯式的互斥鎖:Lock接口,它提供了比synchronized更加普遍的鎖定操做。Lock接口有3個實現它的類:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入鎖、讀鎖和寫鎖。lock必須被顯式地建立、鎖定和釋放,爲了可使用更多的功能,通常用ReentrantLock爲其實例化。爲了保證鎖最終必定會被釋放(可能會有異常發生),要把互斥區放在try語句塊內,並在finally語句塊中釋放鎖,尤爲當有return語句時,return語句必須放在try字句中,以確保unlock()不會過早發生,從而將數據暴露給第二個任務。所以,採用lock加鎖和釋放鎖的通常形式以下:算法
Lock lock = new ReentrantLock();//默認使用非公平鎖,若是要使用公平鎖,須要傳入參數true編程
........安全
lock.lock();多線程
try {併發
//更新對象的狀態性能
//捕獲異常,必要時恢復到原來的不變約束優化
//若是有return語句,放在這裏spa
} finally {.net
lock.unlock(); //鎖必須在finally塊中釋放
}
2、ReetrankLock與synchronized比較
一、性能比較
在JDK1.5中,synchronized是性能低效的。由於這是一個重量級操做,它對性能最大的影響是阻塞的是實現,掛起線程和恢復線程的操做都須要轉入內核態中完成,這些操做給系統的併發性帶來了很大的壓力。相比之下使用Java提供的Lock對象,性能更高一些。Brian Goetz對這兩種鎖在JDK1.五、單核處理器及雙Xeon處理器環境下作了一組吞吐量對比的實驗,發現多線程環境下,synchronized的吞吐量降低的很是嚴重,而ReentrankLock則能基本保持在同一個比較穩定的水平上。但與其說ReetrantLock性能好,倒不如說synchronized還有很是大的優化餘地,因而到了JDK1.6,發生了變化,對synchronize加入了不少優化措施,有自適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。致使在JDK1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在將來的版本中還有優化餘地,因此仍是提倡在synchronized能實現需求的狀況下,優先考慮使用synchronized來進行同步。
下面淺析如下兩種鎖機制的底層的實現策略。
互斥同步最主要的問題就是進行線程阻塞和喚醒所帶來的性能問題,於是這種同步又稱爲阻塞同步,它屬於一種悲觀的併發策略,即線程得到的是獨佔鎖。獨佔鎖意味着其餘線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引發線程上下文切換,當有不少線程競爭鎖的時候,會引發CPU頻繁的上下文切換致使效率很低。synchronized採用的即是這種併發策略。
隨着指令集的發展,咱們有了另外一種選擇:基於衝突檢測的樂觀併發策略,通俗地講就是先進性操做,若是沒有其餘線程爭用共享數據,那操做就成功了,若是共享數據被爭用,產生了衝突,那就再進行其餘的補償措施(最多見的補償措施就是不斷地重拾,直到試成功爲止),這種樂觀的併發策略的許多實現都不須要把線程掛起,所以這種同步被稱爲非阻塞同步。ReetrantLock採用的即是這種併發策略。
在樂觀的併發策略中,須要操做和衝突檢測這兩個步驟具有原子性,它靠硬件指令來保證,這裏用的是CAS操做(Compare and Swap)。JDK1.5以後,Java程序纔可使用CAS操做。咱們能夠進一步研究ReentrantLock的源代碼,會發現其中比較重要的得到鎖的一個方法是compareAndSetState,這裏其實就是調用的CPU提供的特殊指令。現代的CPU提供了指令,能夠自動更新共享數據,並且可以檢測到其餘線程的干擾,而compareAndSet() 就用這些代替了鎖定。這個算法稱做非阻塞算法,意思是一個線程的失敗或者掛起不該該影響其餘線程的失敗或掛起。
Java 5中引入了注入AutomicInteger、AutomicLong、AutomicReference等特殊的原子性變量類,它們提供的如:compareAndSet()、incrementAndSet()和getAndIncrement()等方法都使用了CAS操做。所以,它們都是由硬件指令來保證的原子方法。
二、用途比較
基本語法上,ReentrantLock與synchronized很類似,它們都具有同樣的線程重入特性,只是代碼寫法上有點區別而已,一個表現爲API層面的互斥鎖(Lock),一個表現爲原生語法層面的互斥鎖(synchronized)。ReentrantLock相對synchronized而言仍是增長了一些高級功能,主要有如下三項:
一、等待可中斷:當持有鎖的線程長期不釋放鎖時,正在等待的線程能夠選擇放棄等待,改成處理其餘事情,它對處理執行時間很是上的同步塊頗有幫助。而在等待由synchronized產生的互斥鎖時,會一直阻塞,是不能被中斷的。
二、可實現公平鎖:多個線程在等待同一個鎖時,必須按照申請鎖的時間順序排隊等待,而非公平鎖則不保證這點,在鎖釋放時,任何一個等待鎖的線程都有機會得到鎖。synchronized中的鎖時非公平鎖,ReentrantLock默認狀況下也是非公平鎖,但能夠經過構造方法ReentrantLock(ture)來要求使用公平鎖。
三、鎖能夠綁定多個條件:ReentrantLock對象能夠同時綁定多個Condition對象(名曰:條件變量或條件隊列),而在synchronized中,鎖對象的wait()和notify()或notifyAll()方法能夠實現一個隱含條件,但若是要和多於一個的條件關聯的時候,就不得不額外地添加一個鎖,而ReentrantLock則無需這麼作,只須要屢次調用newCondition()方法便可。並且咱們還能夠經過綁定Condition對象來判斷當前線程通知的是哪些線程(即與Condition對象綁定在一塊兒的其餘線程)。
三、可中斷鎖
ReetrantLock有兩種鎖:忽略中斷鎖和響應中斷鎖。忽略中斷鎖與synchronized實現的互斥鎖同樣,不能響應中斷,而響應中斷鎖能夠響應中斷。
若是某一線程A正在執行鎖中的代碼,另外一線程B正在等待獲取該鎖,可能因爲等待時間過長,線程B不想等待了,想先處理其餘事情,咱們可讓它中斷本身或者在別的線程中中斷它,若是此時ReetrantLock提供的是忽略中斷鎖,則它不會去理會該中斷,而是讓線程B繼續等待,而若是此時ReetrantLock提供的是響應中斷鎖,那麼它便會處理中斷,讓線程B放棄等待,轉而去處理其餘事情。
得到響應中斷鎖的通常形式以下:
ReentrantLock lock = new ReentrantLock();
...........
lock.lockInterruptibly();//獲取響應中斷鎖
try {
//更新對象的狀態
//捕獲異常,必要時恢復到原來的不變約束
//若是有return語句,放在這裏
}finally{
lock.unlock(); //鎖必須在finally塊中釋放
}
3、條件變量實現線程間協做
在生產者——消費者模型一文中,咱們用synchronized實現互斥,並配合使用Object對象的wait()和notify()或notifyAll()方法來實現線程間協做。Java 5以後,咱們能夠用Reentrantlock鎖配合Condition對象上的await()和signal()或signalAll()方法來實現線程間協做。在ReentrantLock對象上newCondition()能夠獲得一個Condition對象,能夠經過在Condition上調用await()方法來掛起一個任務(線程),經過在Condition上調用signal()來通知任務,從而喚醒一個任務,或者調用signalAll()來喚醒全部在這個Condition上被其自身掛起的任務。另外,若是使用了公平鎖,signalAll()的與Condition關聯的全部任務將以FIFO隊列的形式獲取鎖,若是沒有使用公平鎖,則獲取鎖的任務是隨機的,這樣咱們即可以更好地控制處在await狀態的任務獲取鎖的順序。與notifyAll()相比,signalAll()是更安全的方式。另外,它能夠指定喚醒與自身Condition對象綁定在一塊兒的任務。Lock和Condition對象只有在更加困難的多線程問題中才是必須的。
讀寫鎖
另外,synchronized獲取的互斥鎖不只互斥讀寫操做、寫寫操做,還互斥讀讀操做,而讀讀操做時不會帶來數據競爭的,所以對對讀讀操做也互斥的話,會下降性能。Java 5中提供了讀寫鎖,它將讀鎖和寫鎖分離,使得讀讀操做不互斥,獲取讀鎖和寫鎖的通常形式以下:
ReadWriteLock rwl = new ReentrantReadWriteLock();
rwl.writeLock().lock() //獲取寫鎖
rwl.readLock().lock() //獲取讀鎖
用讀鎖來鎖定讀操做,用寫鎖來鎖定寫操做,這樣寫操做和寫操做之間會互斥,讀操做和寫操做之間會互斥,但讀操做和讀操做就不會互斥。
《Java併發編程實踐》一書給出了使用 ReentrantLock的最佳時機:
當你須要如下高級特性時,才應該使用:可定時的、可輪詢的與可中斷的鎖獲取操做,公平隊列,或者非塊結構的鎖。不然,請使用synchronized。