各種鎖的對比總結

參考

《java併發編程的藝術》
《七週七併發模型》

多線程併發編程中最常用的就是synchronized和volatile兩個關鍵字。

volatile

通常被描述成一個輕量級鎖。
用於聲明需要在多線程環境中共享的對象。 只能在類實例對象上聲明。
功能:
保證當前對象對其它線程可見
禁止代碼的重排序

重排序 :代碼中上下兩段代碼不存在依賴關係時,jvm會對代碼進行優化排序,排序後的順序不一定是代碼的順序
內存屏障 : 一組cpu指令,用於實現對內存操作的順序限制

但是volatile並不能保證線程的安全性

q1:volatile 如何保證可見性
volatile聲明的變量在轉變成彙編語言在底層執行的時候會多出lock的指令。
該指令會引發兩個事情:

  1. 將當前線程的工作內存中的數據寫回到主內存中
  2. 寫回內存的操作會讓其它線程工作內存中緩存了該字段的數據無效

Synchronized

通常被描述成重量級鎖,也可稱爲內置鎖
可用於方法,對象,代碼段上
功能 :保證當前操作的線程安全性
保證可見性

底層實現:
synchronized是通過對象的monitor的monitorenter和monitorexit來實現對象鎖的持有和釋放
每個對象上都有一個java 對象頭,用來存放當前對象的鎖信息,hashcode, 分代年齡,是否偏向鎖,鎖標誌位等信息。
鎖按級別從低到高有四種狀態:無鎖,偏向鎖,輕量級鎖,重量級鎖
如圖所示:
這裏寫圖片描述
缺點:

  1. 在獲取鎖而進入阻塞狀態時,是無法中斷的
  2. 嘗試獲取內置鎖時,無法設置超時時間
  3. 獲得內置鎖,必須使用Synchronized塊

導致的主要問題就是:
死鎖而無法恢復,唯一解決方法是 終止jvm

爲了解決內置鎖的問題,引入reentrantLock(可重入鎖)

可重入鎖 ReentrantLock

優勢:

  1. 顯示的加鎖和解鎖
        Lock lock = new ReentrantLock();
        lock.lock();
        try{
            ...
        }finally{
            lock.unlock()
        }
  1. 可中斷 lock.lockInterruptibly()
  2. 可設置超時時間 lock.tryLock(1000, TimeUnit.MILLISECONDS)

偏向鎖

是指被同一線程重複獲取的鎖
優點是:
進入和退出鎖的時候不需要cas操作來加鎖,解鎖。效率更快

這裏寫圖片描述

如何關閉偏向鎖
java1.6, 1.7是默認啓用的,
jvm參數: -XX:BiasedLockingStartupDelay = 0

輕量級鎖的獲取及膨脹流程
這裏寫圖片描述

鎖的優缺點對比

優點 缺點 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗,和執行非同步方法相比僅存在納米級的差距 如果線程之間存在鎖競爭會帶來額外的鎖撤銷的消耗 適用於只有一個線程訪問同步塊的情況
輕量級鎖 競爭的線程不會阻塞,提高線程的響應速度 如果始終得不到鎖競爭的線程,使用自旋迴消耗cpu 追求響應時間,同步塊執行速度非常快
重量級鎖 線程競爭不使用自旋,不會消耗cpu 線程阻塞,響應時間緩慢 追求吞吐量,同步執行時間較長

三者之間的區分,如果只有一個線程重複進入同步塊,那是偏向鎖,如果多個線程進入同步塊,不會阻塞,那是輕量級鎖,會阻塞,是重量級鎖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章