JAVA中的樂觀鎖和悲觀鎖

  • 樂觀鎖:當處理數據的時候,樂觀的認爲處理數據過程不會發送多線程衝突,代表AtomicInteger 的CAS(Compare And Swape) ,就是處理數據的時候不會對本段處理過程進行加鎖,當更新數據的時候再進行判定數據是否已經發送了衝突或許修改,如果已經衝突則返回失敗信息交給用戶處理。
  • 悲觀鎖:當處理數據的時候,會認爲任何時候本處理都會受到多線程衝突影響,因此會加鎖進行阻塞處理,例如JAVA中的同步鎖 synchronized。
  • 悲觀鎖存在的問題:
    1、處理需要加鎖和解鎖,加鎖解鎖過程需要消耗資源。
    2、當對某一部分進行加鎖之後,其他線程只能阻塞,直到本線程處理完畢之後纔有機會去競爭鎖。
    3、當低線程獲得鎖之後還在處理,優先級高的線程到達之後也需要阻塞等待,因此導致優先級高低線程倒掛處理問題。
  • 樂觀鎖可能存在的問題(CAS只是樂觀鎖的一種技術)。
    1、CAS有自旋的風險,因爲如果不成功就回循環的來獲取,直到獲取成功爲止;那麼這裏在數據量非常大的時候,就有問題了。可能因爲數據量過大,導致循環時間過長,從而大量佔用了CPU資源。
    2、存在讀髒數據的風險。例如ABA的問題,當堆棧中存在ABCD數據,當線程T1 訪問A對其進行處理;這個時候線程T2來了,目的是把B清除棧外。這個時候存在2個棧ACD 和 B。本來線程T1知道A的next是指向B的,但是,這個時候A其實指向的是null,當A迴歸的時候需要把B放入棧定,那麼這個時候CD會直接丟失。
    3.只能保證一個共享變量的原子操作:當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖,或者有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來操作。比如有兩個共享變量i=2,j=a,合併一下ij=2a,然後用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象裏來進行CAS操作。

通過以上分析,得出了悲觀鎖和樂觀鎖使用場景如下:
    1、對於資源競爭較少(線程衝突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗cpu資源;而CAS基於硬件實現,不需要進入內核,不需要切換線程,操作自旋機率較少,因此可以獲得更高的性能。

    2、對於資源競爭嚴重(線程衝突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低於synchronized。

   補充: synchronized在jdk1.6之後,已經改進優化。synchronized的底層實現主要依靠Lock-Free的隊列,基本思路是自旋後阻塞,競爭切換後繼續競爭鎖,稍微犧牲了公平性,但獲得了高吞吐量。在線程衝突較少的情況下,可以獲得和CAS類似的性能;而線程衝突嚴重的情況下,性能遠高於CAS。

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