lock synchronized 區別

http://blog.csdn.net/natian306/article/details/18504111

添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用情況下更佳的性能

一、synchronized和lock的用法區別

 
synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。
 

lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做爲鎖,多個線程中必須要使用一個ReentrantLock類做爲對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。

二、synchronized和lock性能區別
 
synchronized是託管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因爲這是一個重量級操作,需要調用操作接口,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致在Java1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在未來的版本中還有優化餘地。


說到這裏,還是想提一下這2中機制的具體區別。據我所知,synchronized原始採用的是CPU悲觀鎖機制,即線程獲得的是獨佔鎖。獨佔鎖意味着其他線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引起線程上下文切換,當有很多線程競爭鎖的時候,會引起CPU頻繁的上下文切換導致效率很低。
 
Lock用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有衝突而去完成某項操作,如果因爲衝突失敗就重試,直到成功爲止。樂觀鎖實現的機制就是CAS操作(Compare and Swap)。我們可以進一步研究ReentrantLock的源代碼,會發現其中比較重要的獲得鎖的一個方法是compareAndSetState。這裏其實就是調用的CPU提供的特殊指令。
 
現代的CPU提供了指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。這個算法稱作非阻塞算法,意思是一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的算法。
 
我也只是瞭解到這一步,具體到CPU的算法如果感興趣的讀者還可以在查閱下,如果有更好的解釋也可以給我留言,我也學習下

三、synchronized和lock用途區別
 
synchronized原語和ReentrantLock在一般情況下沒有什麼區別,但是在非常複雜的同步應用中,請考慮使用ReentrantLock,特別是遇到下面2種需求的時候。
 
1.某個線程在等待一個鎖的控制權的這段時間需要中斷   lock.lockInterruptibly()
2.需要分開處理一些wait-notify,ReentrantLock裏面的Condition應用,能夠控制notify哪個線程  (http://blog.csdn.net/ghsau/article/details/7481142)
3.具有公平鎖功能,每個到來的線程都將排隊等候

最後呢,ReentrantLock這個類還提供了2種競爭鎖的機制:公平鎖和非公平鎖。這2種機制的意思從字面上也能瞭解個大概:即對於多線程來說,公平鎖會依賴線程進來的順序,後進來的線程後獲得鎖。而非公平鎖的意思就是後進來的鎖也可以和前邊等待鎖的線程同時競爭鎖資源。對於效率來講,當然是非公平鎖效率更高,因爲公平鎖還要判斷是不是線程隊列的第一個纔會讓線程獲得鎖。


1、ReentrantLock 擁有Synchronized相同的併發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候
     線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,
     如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
     如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹別的事情
 
    ReentrantLock獲取鎖定與4種方式:
    a)  lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖
    b) tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
    c)tryLock(long timeout,TimeUnit unit),   如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,
如果獲取了鎖定,就返回true,如果等待超時,返回false;
    d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前線程處於休眠狀態,直到獲得鎖定,或者當前線程被別的線程中斷
 
2、synchronized是在jvm層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,jvm會自動釋放鎖定,
但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中
 
3、在資源競爭不是很激烈的情況下,Synchronized的性能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,
但是ReetrantLock的性能能維持常態;

1、Lock提供了無條件的、可輪詢的、定時的、可中斷的鎖獲取操作,所有加鎖和解鎖的方法都是顯式的。

public interface Lock{

    void lock();

    void lockInterruptibly() throws  InterruptedException;

    boolean tryLock();

    boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();

}

2、ReentrantLock實現了lock接口,跟synchronized相比,ReentrantLock爲處理不可用的鎖提供了更多靈活性。

3、使用lock接口的規範形式要求在finally塊中釋放鎖lock.unlock()。如果鎖守護的代碼在try塊之外拋出了異常,它將永遠不會被釋放。

 

 

Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition 對象。

 

鎖是控制多個線程對共享資源進行訪問的工具。通常,鎖提供了對共享資源的獨佔訪問。一次只能有一個線程獲得鎖,對共享資源的所有訪問都需要首先獲得鎖。不過,某些鎖可能允許對共享資源併發訪問,如 ReadWriteLock 的讀取鎖。

 

synchronized 方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻強制所有鎖獲取和釋放均要出現在一個塊結構中:當獲取了多個鎖時,它們必須以相反的順序釋放,且必須在與所有鎖被獲取時相同的詞法範圍內釋放所有鎖。

 

雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還幫助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更爲靈活的方式使用鎖。例如,某些遍歷併發訪問的數據結果的算法要求使用 "hand-over-hand" 或 "chain locking":獲取節點 A 的鎖,然後再獲取節點 B 的鎖,然後釋放 A 並獲取 C,然後釋放 B 並獲取 D,依此類推。

 

Lock 接口的實現允許鎖在不同的作用範圍內獲取和釋放,並允許以任何順序獲取和釋放多個鎖,從而支持使用這種技術。

 

隨着靈活性的增加,也帶來了更多的責任。不使用塊結構鎖就失去了使用 synchronized 方法和語句時會出現的鎖自動釋放功能。

 

在大多數情況下,應該使用以下語句:

     Lock l = ...;
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }


 鎖定和取消鎖定出現在不同作用範圍中時,必須謹慎地確保保持鎖定時所執行的所有代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。

 


Lock 實現提供了使用 synchronized 方法和語句所沒有的其他功能,包括提供了一個非塊結構的獲取鎖嘗試 (tryLock())、一個獲取可中斷鎖的嘗試 (lockInterruptibly()) 和一個獲取超時失效鎖的嘗試 (tryLock(long, TimeUnit))。

 

 

Lock 類還可以提供與隱式監視器鎖完全不同的行爲和語義,如保證排序、非重入用法或死鎖檢測。如果某個實現提供了這樣特殊的語義,則該實現必須對這些語義加以記錄。

注意,Lock 實例只是普通的對象,其本身可以在 synchronized 語句中作爲目標使用。獲取 Lock 實例的監視器鎖與調用該實例的任何 lock() 方法沒有特別的關係。爲了避免混淆,建議除了在其自身的實現中之外,決不要以這種方式使用 Lock 實例。

除非另有說明,否則爲任何參數傳遞 null 值都將導致拋出 NullPointerException。

內存同步


所有 Lock 實現都必須 實施與內置監視器鎖提供的相同內存同步語義,如 The Java Language Specification, Third Edition (17.4 Memory Model) 中所描述的:

成功的 lock 操作與成功的 Lock 操作具有同樣的內存同步效應。
成功的 unlock 操作與成功的 Unlock 操作具有同樣的內存同步效應。

 


不成功的鎖定與取消鎖定操作以及重入鎖定/取消鎖定操作都不需要任何內存同步效果。


實現注意事項


三種形式的鎖獲取(可中斷、不可中斷和定時)在其性能特徵、排序保證或其他實現質量上可能會有所不同。而且,對於給定的 Lock 類,可能沒有中斷正在進行的 鎖獲取的能力。因此,並不要求實現爲所有三種形式的鎖獲取定義相同的保證或語義,也不要求其支持中斷正在進行的鎖獲取。實現必需清楚地對每個鎖定方法所提供的語義和保證進行記錄。還必須遵守此接口中定義的中斷語義,以便爲鎖獲取中斷提供支持:完全支持中斷,或僅在進入方法時支持中斷。

 

由於中斷通常意味着取消,而通常又很少進行中斷檢查,因此,相對於普通方法返回而言,實現可能更喜歡響應某個中斷。即使出現在另一個操作後的中斷可能會釋放線程鎖時也是如此。實現應記錄此行爲。


發佈了69 篇原創文章 · 獲贊 16 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章