<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖解Java中那18 把鎖

          共 6210字,需瀏覽 13分鐘

           ·

          2021-06-24 03:08

          • 樂觀鎖和悲觀鎖
          • 獨占鎖和共享鎖
          • 互斥鎖和讀寫鎖
          • 公平鎖和非公平鎖
          • 可重入鎖
          • 自旋鎖
          • 分段鎖
          • 鎖升級(無鎖|偏向鎖|輕量級鎖|重量級鎖)
          • 鎖優(yōu)化技術(shù)(鎖粗化、鎖消除)

          樂觀鎖和悲觀鎖

          悲觀鎖

          悲觀鎖對應(yīng)于生活中悲觀的人,悲觀的人總是想著事情往壞的方向發(fā)展。

          舉個生活中的例子,假設(shè)廁所只有一個坑位了,悲觀鎖上廁所會第一時間把門反鎖上,這樣其他人上廁所只能在門外等候,這種狀態(tài)就是「阻塞」了。

          回到代碼世界中,一個共享數(shù)據(jù)加了悲觀鎖,那線程每次想操作這個數(shù)據(jù)前都會假設(shè)其他線程也可能會操作這個數(shù)據(jù),所以每次操作前都會上鎖,這樣其他線程想操作這個數(shù)據(jù)拿不到鎖只能阻塞了。

          在 Java 語言中 synchronizedReentrantLock等就是典型的悲觀鎖,還有一些使用了 synchronized 關(guān)鍵字的容器類如 HashTable 等也是悲觀鎖的應(yīng)用。

          樂觀鎖

          樂觀鎖 對應(yīng)于生活中樂觀的人,樂觀的人總是想著事情往好的方向發(fā)展。

          舉個生活中的例子,假設(shè)廁所只有一個坑位了,樂觀鎖認(rèn)為:這荒郊野外的,又沒有什么人,不會有人搶我坑位的,每次關(guān)門上鎖多浪費時間,還是不加鎖好了。你看樂觀鎖就是天生樂觀!

          回到代碼世界中,樂觀鎖操作數(shù)據(jù)時不會上鎖,在更新的時候會判斷一下在此期間是否有其他線程去更新這個數(shù)據(jù)。

          樂觀鎖可以使用版本號機(jī)制CAS算法實現(xiàn)。在 Java 語言中 java.util.concurrent.atomic包下的原子類就是使用CAS 樂觀鎖實現(xiàn)的。

          兩種鎖的使用場景

          悲觀鎖和樂觀鎖沒有孰優(yōu)孰劣,有其各自適應(yīng)的場景。

          樂觀鎖適用于寫比較少(沖突比較小)的場景,因為不用上鎖、釋放鎖,省去了鎖的開銷,從而提升了吞吐量。

          如果是寫多讀少的場景,即沖突比較嚴(yán)重,線程間競爭激勵,使用樂觀鎖就是導(dǎo)致線程不斷進(jìn)行重試,這樣可能還降低了性能,這種場景下使用悲觀鎖就比較合適。

          獨占鎖和共享鎖

          獨占鎖

          獨占鎖是指鎖一次只能被一個線程所持有。如果一個線程對數(shù)據(jù)加上排他鎖后,那么其他線程不能再對該數(shù)據(jù)加任何類型的鎖。獲得獨占鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。

          JDK中的synchronizedjava.util.concurrent(JUC)包中Lock的實現(xiàn)類就是獨占鎖。

          共享鎖

          共享鎖是指鎖可被多個線程所持有。如果一個線程對數(shù)據(jù)加上共享鎖后,那么其他線程只能對數(shù)據(jù)再加共享鎖,不能加獨占鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。

          在 JDK 中 ReentrantReadWriteLock 就是一種共享鎖。

          互斥鎖和讀寫鎖

          互斥鎖

          互斥鎖是獨占鎖的一種常規(guī)實現(xiàn),是指某一資源同時只允許一個訪問者對其進(jìn)行訪問,具有唯一性和排它性。

          互斥鎖一次只能一個線程擁有互斥鎖,其他線程只有等待。

          讀寫鎖

          讀寫鎖是共享鎖的一種具體實現(xiàn)。讀寫鎖管理一組鎖,一個是只讀的鎖,一個是寫鎖。

          讀鎖可以在沒有寫鎖的時候被多個線程同時持有,而寫鎖是獨占的。寫鎖的優(yōu)先級要高于讀鎖,一個獲得了讀鎖的線程必須能看到前一個釋放的寫鎖所更新的內(nèi)容。

          讀寫鎖相比于互斥鎖并發(fā)程度更高,每次只有一個寫線程,但是同時可以有多個線程并發(fā)讀。

          在 JDK 中定義了一個讀寫鎖的接口:ReadWriteLock

          public interface ReadWriteLock {
              /**
               * 獲取讀鎖
               */

              Lock readLock();

              /**
               * 獲取寫鎖
               */

              Lock writeLock();
          }

          ReentrantReadWriteLock 實現(xiàn)了ReadWriteLock接口,具體實現(xiàn)這里不展開,后續(xù)會深入源碼解析。

          公平鎖和非公平鎖

          公平鎖

          公平鎖是指多個線程按照申請鎖的順序來獲取鎖,這里類似排隊買票,先來的人先買,后來的人在隊尾排著,這是公平的。

          在 java 中可以通過構(gòu)造函數(shù)初始化公平鎖

          /**
          * 創(chuàng)建一個可重入鎖,true 表示公平鎖,false 表示非公平鎖。默認(rèn)非公平鎖
          */

          Lock lock = new ReentrantLock(true);

          非公平鎖

          非公平鎖是指多個線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取鎖,在高并發(fā)環(huán)境下,有可能造成優(yōu)先級翻轉(zhuǎn),或者饑餓的狀態(tài)(某個線程一直得不到鎖)。

          在 java 中 synchronized 關(guān)鍵字是非公平鎖,ReentrantLock默認(rèn)也是非公平鎖。

          /**
          * 創(chuàng)建一個可重入鎖,true 表示公平鎖,false 表示非公平鎖。默認(rèn)非公平鎖
          */

          Lock lock = new ReentrantLock(false);

          可重入鎖

          可重入鎖又稱之為遞歸鎖,是指同一個線程在外層方法獲取了鎖,在進(jìn)入內(nèi)層方法會自動獲取鎖。

          對于Java ReentrantLock而言, 他的名字就可以看出是一個可重入鎖。對于Synchronized而言,也是一個可重入鎖。

          敲黑板:可重入鎖的一個好處是可一定程度避免死鎖。

          以 synchronized 為例,看一下下面的代碼:

          public synchronized void mehtodA() throws Exception{
           // Do some magic tings
           mehtodB();
          }

          public synchronized void mehtodB() throws Exception{
           // Do some magic tings
          }

          上面的代碼中 methodA 調(diào)用 methodB,如果一個線程調(diào)用methodA 已經(jīng)獲取了鎖再去調(diào)用 methodB 就不需要再次獲取鎖了,這就是可重入鎖的特性。如果不是可重入鎖的話,mehtodB 可能不會被當(dāng)前線程執(zhí)行,可能造成死鎖。

          自旋鎖

          自旋鎖是指線程在沒有獲得鎖時不是被直接掛起,而是執(zhí)行一個忙循環(huán),這個忙循環(huán)就是所謂的自旋。

          自旋鎖的目的是為了減少線程被掛起的幾率,因為線程的掛起和喚醒也都是耗資源的操作。

          如果鎖被另一個線程占用的時間比較長,即使自旋了之后當(dāng)前線程還是會被掛起,忙循環(huán)就會變成浪費系統(tǒng)資源的操作,反而降低了整體性能。因此自旋鎖是不適應(yīng)鎖占用時間長的并發(fā)情況的。

          在 Java 中,AtomicInteger 類有自旋的操作,我們看一下代碼:

          public final int getAndAddInt(Object o, long offset, int delta) {
              int v;
              do {
                  v = getIntVolatile(o, offset);
              } while (!compareAndSwapInt(o, offset, v, v + delta));
              return v;
          }

          CAS 操作如果失敗就會一直循環(huán)獲取當(dāng)前 value 值然后重試。

          另外自適應(yīng)自旋鎖也需要了解一下。

          在JDK1.6又引入了自適應(yīng)自旋,這個就比較智能了,自旋時間不再固定,由前一次在同一個鎖上的自旋時間以及鎖的擁有者的狀態(tài)來決定。如果虛擬機(jī)認(rèn)為這次自旋也很有可能再次成功那就會次序較多的時間,如果自旋很少成功,那以后可能就直接省略掉自旋過程,避免浪費處理器資源。

          分段鎖

          分段鎖 是一種鎖的設(shè)計,并不是具體的一種鎖。

          分段鎖設(shè)計目的是將鎖的粒度進(jìn)一步細(xì)化,當(dāng)操作不需要更新整個數(shù)組的時候,就僅僅針對數(shù)組中的一項進(jìn)行加鎖操作。

          在 Java 語言中 CurrentHashMap 底層就用了分段鎖,使用Segment,就可以進(jìn)行并發(fā)使用了。

          鎖升級(無鎖|偏向鎖|輕量級鎖|重量級鎖)

          JDK1.6 為了提升性能減少獲得鎖和釋放鎖所帶來的消耗,引入了4種鎖的狀態(tài):無鎖、偏向鎖、輕量級鎖重量級鎖,它會隨著多線程的競爭情況逐漸升級,但不能降級。

          無鎖

          無鎖狀態(tài)其實就是上面講的樂觀鎖,這里不再贅述。

          偏向鎖

          Java偏向鎖(Biased Locking)是指它會偏向于第一個訪問鎖的線程,如果在運行過程中,只有一個線程訪問加鎖的資源,不存在多線程競爭的情況,那么線程是不需要重復(fù)獲取鎖的,這種情況下,就會給線程加一個偏向鎖。

          偏向鎖的實現(xiàn)是通過控制對象Mark Word的標(biāo)志位來實現(xiàn)的,如果當(dāng)前是可偏向狀態(tài),需要進(jìn)一步判斷對象頭存儲的線程 ID 是否與當(dāng)前線程 ID 一致,如果一致直接進(jìn)入。

          輕量級鎖

          當(dāng)線程競爭變得比較激烈時,偏向鎖就會升級為輕量級鎖,輕量級鎖認(rèn)為雖然競爭是存在的,但是理想情況下競爭的程度很低,通過自旋方式等待上一個線程釋放鎖。

          重量級鎖

          如果線程并發(fā)進(jìn)一步加劇,線程的自旋超過了一定次數(shù),或者一個線程持有鎖,一個線程在自旋,又來了第三個線程訪問時(反正就是競爭繼續(xù)加大了),輕量級鎖就會膨脹為重量級鎖,重量級鎖會使除了此時擁有鎖的線程以外的線程都阻塞。

          升級到重量級鎖其實就是互斥鎖了,一個線程拿到鎖,其余線程都會處于阻塞等待狀態(tài)。

          在 Java 中,synchronized 關(guān)鍵字內(nèi)部實現(xiàn)原理就是鎖升級的過程:無鎖 --> 偏向鎖 --> 輕量級鎖 --> 重量級鎖。這一過程在后續(xù)講解 synchronized 關(guān)鍵字的原理時會詳細(xì)介紹。

          鎖優(yōu)化技術(shù)(鎖粗化、鎖消除)

          鎖粗化

          鎖粗化就是將多個同步塊的數(shù)量減少,并將單個同步塊的作用范圍擴(kuò)大,本質(zhì)上就是將多次上鎖、解鎖的請求合并為一次同步請求。

          舉個例子,一個循環(huán)體中有一個代碼同步塊,每次循環(huán)都會執(zhí)行加鎖解鎖操作。

          private static final Object LOCK = new Object();

          for(int i = 0;i < 100; i++) {
              synchronized(LOCK){
                  // do some magic things
              }
          }

          經(jīng)過鎖粗化后就變成下面這個樣子了:

           synchronized(LOCK){
               for(int i = 0;i < 100; i++) {
                  // do some magic things
              }
          }

          鎖消除

          鎖消除是指虛擬機(jī)編譯器在運行時檢測到了共享數(shù)據(jù)沒有競爭的鎖,從而將這些鎖進(jìn)行消除。

          舉個例子讓大家更好理解。

          public String test(String s1, String s2){
              StringBuffer stringBuffer = new StringBuffer();
              stringBuffer.append(s1);
              stringBuffer.append(s2);
              return stringBuffer.toString();
          }

          上面代碼中有一個 test 方法,主要作用是將字符串 s1 和字符串 s2 串聯(lián)起來。

          test 方法中三個變量s1, s2, stringBuffer, 它們都是局部變量,局部變量是在棧上的,棧是線程私有的,所以就算有多個線程訪問 test 方法也是線程安全的。

          我們都知道 StringBuffer 是線程安全的類,append 方法是同步方法,但是 test 方法本來就是線程安全的,為了提升效率,虛擬機(jī)幫我們消除了這些同步鎖,這個過程就被稱為鎖消除。

          StringBuffer.class

          // append 是同步方法
          public synchronized StringBuffer append(String str
          {
              toStringCache = null;
              super.append(str);
              return this;
          }

          一張圖總結(jié):

          前面講了 Java 語言中各種各種的鎖,最后再通過六個問題統(tǒng)一總結(jié)一下:

          — 【 THE END 】—
          本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲?。?/span>

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關(guān)注公眾號并回復(fù) PDF 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 65
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产l精品久久久久久久久久 | 俺来也俺去也 | 操b精品 操逼看看 | 操骚逼黄色网址 | 啪啪黄色视频 |