Java15種鎖(轉(zhuǎn)載)

公平鎖/非公平鎖
  • 公平鎖是指多個線程按照申請鎖的順序來獲取鎖
  • 非公平鎖是指多個線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取鎖。有可能,會造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象

對于ReentrantLock而言,通過構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認是非公平鎖。非公平鎖的優(yōu)點在于吞吐量比公平鎖大。
對于Synchronized而言,也是一種非公平鎖。由于其并不像ReentrantLock是通過AQS的來實現(xiàn)線程調(diào)度,所以并沒有任何辦法使其變成公平鎖

可重入鎖/非可重入鎖
  • 可重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數(shù)獲得鎖之后 ,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響;
```
 synchronized void setA() throws Exception{
     Thread.sleep(1000);
      setB();
  }
  synchronized void setB() throws Exception{
     Thread.sleep(1000);
 }
```
  • 不可重入鎖,與可重入鎖相反,不可遞歸調(diào)用,遞歸調(diào)用就發(fā)生死鎖??吹揭粋€經(jīng)典的講解,使用自旋鎖來模擬一個不可重入鎖;
     public class UnreentrantLock {
             private AtomicReference owner = new AtomicReference();
             public void lock() {
                   Thread current = Thread.currentThread();
                   //這句是很經(jīng)典的“自旋”語法,AtomicInteger中也有
                   for (;;) {
                         if (!owner.compareAndSet(null, current)) {
                         return;
                          }
                  }
            }
            public void unlock() {
                  Thread current = Thread.currentThread();
                  owner.compareAndSet(current, null);
            }
    }

ReentrantLock和synchronized都是可重入鎖

獨享鎖/共享鎖
  • 獨享鎖:該鎖每一次只能被一個線程所持有
  • 共享鎖:該鎖可被多個線程共有,典型的就是ReentrantReadWriteLock里的讀鎖,它的讀鎖是可以被共享的,但是它的寫鎖確每次只能被獨占
    另外讀鎖的共享可保證并發(fā)讀是非常高效的,但是讀寫和寫寫,寫讀都是互斥的。

獨享鎖與共享鎖也是通過AQS來實現(xiàn)的,通過實現(xiàn)不同的方法,來實現(xiàn)獨享或者共享。
對于Synchronized而言,當(dāng)然是獨享鎖。

互斥鎖/讀寫鎖
  • 互斥鎖:在訪問共享資源之前對進行加鎖操作,在訪問完成之后進行解鎖操作。 加鎖后,任何其他試圖再次加鎖的線程會被阻塞,直到當(dāng)前進程解鎖
    如果解鎖時有一個以上的線程阻塞,那么所有該鎖上的線程都被編程就緒狀態(tài), 第一個變?yōu)榫途w狀態(tài)的線程又執(zhí)行加鎖操作,那么其他的線程又會進入等待。 在這種方式下,只有一個線程能夠訪問被互斥鎖保護的資源
  • 讀寫鎖: 讀寫鎖既是互斥鎖,又是共享鎖,read模式是共享,write是互斥(排它鎖)的。
    讀寫鎖有三種狀態(tài):讀加鎖狀態(tài)、寫加鎖狀態(tài)和不加鎖狀態(tài)

讀寫鎖在Java中的具體實現(xiàn)就是ReadWriteLock

一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可以同時占有讀模式的讀寫鎖。

只有一個線程可以占有寫狀態(tài)的鎖,但可以有多個線程同時占有讀狀態(tài)鎖,這也是它可以實現(xiàn)高并發(fā)的原因。當(dāng)其處于寫狀態(tài)鎖下,任何想要嘗試獲得鎖的線程都會被阻塞,直到寫狀態(tài)鎖被釋放;如果是處于讀狀態(tài)鎖下,允許其它線程獲得它的讀狀態(tài)鎖,但是不允許獲得它的寫狀態(tài)鎖,直到所有線程的讀狀態(tài)鎖被釋放;為了避免想要嘗試寫操作的線程一直得不到寫狀態(tài)鎖,當(dāng)讀寫鎖感知到有線程想要獲得寫狀態(tài)鎖時,便會阻塞其后所有想要獲得讀狀態(tài)鎖的線程。所以讀寫鎖非常適合資源的讀操作遠多于寫操作的情況。

悲觀鎖/樂觀鎖
  • 悲觀鎖: 總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時候都認為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現(xiàn)
  • 樂觀鎖:總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號機制和CAS算法實現(xiàn)。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現(xiàn)方式CAS實現(xiàn)的。
分段鎖

分段鎖其實是一種鎖的設(shè)計,并不是具體的一種鎖,對于ConcurrentHashMap而言,其并發(fā)的實現(xiàn)就是通過分段鎖的形式來實現(xiàn)高效的并發(fā)操作。

偏向鎖/輕量級鎖/重量級鎖

鎖的狀態(tài):
1.無鎖狀態(tài)
2.偏向鎖狀態(tài)
3.輕量級鎖狀態(tài)
4.重量級鎖狀態(tài)
鎖的狀態(tài)是通過對象監(jiān)視器在對象頭中的字段來表明的。

四種狀態(tài)會隨著競爭的情況逐漸升級,而且是不可逆的過程,即不可降級。
這四種狀態(tài)都不是Java語言中的鎖,而是Jvm為了提高鎖的獲取與釋放效率而做的優(yōu)化(使用synchronized時)。

  • 偏向鎖
    偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖。降低獲取鎖的代價。
  • 輕量級
    輕量級鎖是指當(dāng)鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。
  • 重量級鎖
    重量級鎖是指當(dāng)鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時候,還沒有獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進入阻塞,性能降低。
自旋鎖

自旋鎖(spinlock):是指當(dāng)一個線程在獲取鎖的時候,如果鎖已經(jīng)被其它線程獲取,那么該線程將循環(huán)等待,然后不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖才會退出循環(huán)。

它是為實現(xiàn)保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖。但是兩者在調(diào)度機制上略有不同。對于互斥鎖,如果資源已經(jīng)被占用,資源申請者只能進入睡眠狀態(tài)。但是自旋鎖不會引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,”自旋”一詞就是因此而得名。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容