1. 程式人生 > 其它 >自旋鎖、阻塞鎖、可重入鎖使用解析

自旋鎖、阻塞鎖、可重入鎖使用解析

常聽見有人在問這幾種型別的鎖有什麼區別,所以整理了這篇文章。

一、自旋鎖

自旋鎖是採用讓當前執行緒不停地的在迴圈體內執行實現的,當迴圈的條件被其他執行緒改變時 才能進入臨界區。如下:

使用了CAS原子操作,lock函式將owner設定為當前執行緒,並且預測原來的值為空。unlock函式將owner設定為null,並且預測值為當前執行緒。

當有第二個執行緒呼叫lock操作時由於owner值不為空,導致迴圈一直被執行,直至第一個執行緒呼叫unlock函式將owner設定為null,第二個執行緒才能進入臨界區。

TIP: 自旋鎖只是將當前執行緒不停地執行迴圈體,不進行執行緒狀態的改變,所以響應速度更快。但當執行緒數不停增加時,效能下降明顯,因為每個執行緒都需要執行,佔用

CPU時間。所以當執行緒競爭不激烈,並且保持鎖的時間段,適合使用自旋鎖。

二、阻塞鎖

阻塞鎖,與自旋鎖不同,改變了執行緒的執行狀態。阻塞鎖,可以說是讓執行緒進入阻塞狀態進行等待,當獲得相應的訊號(喚醒,時間) 時,才可以進入執行緒的準備就緒狀態,準備就緒狀態的所有執行緒,通過競爭,進入執行狀態。

JAVA環境中,執行緒Thread有如下幾個狀態:

  • 新建狀態
  • 就緒狀態
  • 執行狀態
  • 阻塞狀態
  • 死亡狀態

JAVA中,能夠進入\退出、阻塞狀態或包含阻塞鎖的方法有 ,synchronized 關鍵字,ReentrantLockObject.wait()\notify()LockSupport.park() / unpark()

等。

參考Jdk文件中的一個示例就是使用了LockSupport的阻塞鎖:

阻塞鎖的優勢在於,阻塞的執行緒不會佔用cpu時間, 不會導致 cpu佔用率過高,但進入時間以及恢復時間都要比自旋鎖略慢,因為執行緒的狀態需要切換。

TIP:在競爭激烈的情況下 阻塞鎖的效能要明顯高於 自旋鎖。所以線上程競爭不激烈的情況下,使用自旋鎖,競爭激烈的情況下使用,阻塞鎖。

三、可重入鎖

可重入鎖,也叫做遞迴鎖,指的是同一執行緒 外層函式獲得鎖之後 ,內層遞迴函式仍然有獲取該鎖的程式碼,但不受影響。在JDKReentrantLock synchronized 都是可重入鎖。

兩個使用示例:

上面兩個示例都會打印出兩次相同的結果。所以這樣的鎖叫可重入鎖。可重入鎖最大的作用是避免死鎖

如果我們以上面示例的自旋鎖,在同一個執行緒內,兩次呼叫lock(),會導致第二次呼叫lock位置進行自旋,產生了死鎖。當unlock()第一次呼叫時,就已經將所有鎖都釋放。說明這個鎖並不是可重入的。

我們可以把上面的自旋鎖,採用計數的方式改造成可重入鎖。

修改之後,如下: