<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各種鎖

          共 18177字,需瀏覽 37分鐘

           ·

          2021-04-29 17:31

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          76套java從入門到精通實(shí)戰(zhàn)課程分享

          java有哪些鎖的分類:

          1. 悲觀與樂觀鎖

          2. 公平鎖與非公平鎖

          3. 自旋鎖/重入鎖

          4. 重量級(jí)鎖與輕量級(jí)鎖

          5. 獨(dú)占鎖與共享鎖

          樂觀鎖悲觀鎖:

          什么是悲觀鎖,什么是樂觀鎖

          悲觀鎖:

          • mysql的角度分析: 悲觀鎖就是比較悲觀,當(dāng)多個(gè)線程同一個(gè)數(shù)據(jù)實(shí)現(xiàn)修改的時(shí)候,最后只有一個(gè)線程才能修改成功,只要誰(shuí)能夠獲取到行鎖 則其他線程時(shí)不能夠?qū)?shù)據(jù)做任何修改操作,且是阻塞狀態(tài)

          • java鎖層面:如果沒有獲取到鎖,則會(huì)阻塞等待,后期喚醒的鎖成本就會(huì)非常高,從新被我們CPU從就緒調(diào)度為運(yùn)行狀態(tài)

          Lock synchronized 鎖 悲觀鎖 沒有獲取到鎖的線程會(huì)阻塞等待;

          樂觀鎖:

          樂觀鎖比較樂觀,通過預(yù)值或者版本號(hào)比較,如果不一致性的情況則通過循環(huán)控制修改,當(dāng) 前線程不會(huì)被阻塞,是樂觀,效率比較高,但是樂觀鎖比較消耗 cpu 的資源。



          樂觀鎖:獲取鎖–如果沒有獲取到鎖,當(dāng)前線程是不會(huì)阻塞等待,通過死循環(huán)控制

          樂觀鎖屬于無鎖機(jī)制,沒有競(jìng)爭(zhēng)鎖的流程

          注意:mysql 的 innodb 引擎中存在行鎖的概念/

          Mysql層面如何實(shí)現(xiàn)樂觀鎖了?

          在我們表結(jié)構(gòu)中,會(huì)新增一個(gè)字段就是版字段

          ``version varchar(255) DEFAULT NULL,`

          多線程對(duì)同一行數(shù)據(jù)實(shí)現(xiàn)修改操作,提前查詢當(dāng)前最新的version版本號(hào)碼

          作為update條件查詢,如果當(dāng)前version版本號(hào)碼發(fā)生了變化,則查詢不到該數(shù)據(jù),

          表示如果修改數(shù)據(jù)失敗,則不斷重試,有從新查詢最新的版本實(shí)現(xiàn)update

          需要注意的是 控制樂觀鎖的循環(huán)的次數(shù),避免cpu飆高的問題

          mysql的innodb引擎中存在行鎖的概念


          樂觀鎖的實(shí)現(xiàn)方式:

          樂觀鎖,使用版本標(biāo)識(shí)來確定讀到的數(shù)據(jù)與提交時(shí)的數(shù)據(jù)是否一致。提交后修改版本標(biāo)識(shí),不一致時(shí)可以采取丟棄和再次嘗試的策略。
          記錄1,id,status1,status2,stauts3,version,表示有三個(gè)不同的狀態(tài),以及數(shù)據(jù)當(dāng)前的版本
          操作1:update table set status1=1,status2=0,status3=0 where id=111;
          操作2:update table set status1=0,status2=1,status3=0 where id=111;
          操作3:update table set status1=0,status2=0,status3=1 where id=111;
          沒有任何控制的情況下,順序執(zhí)行3個(gè)操作,最后前兩個(gè)操作會(huì)被直接覆蓋。
          加上version字段,每一次的操作都會(huì)更新version,提交時(shí)如果version不匹配,停止本次提交,可以嘗試下一次的提交,以保證拿到的是操作1提交后的結(jié)果。
          這是一種經(jīng)典的樂觀鎖實(shí)現(xiàn)。


          另外,java中的compareandswap即cas,解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制。
          CAS操作包含三個(gè)操作數(shù),內(nèi)存位置(V),預(yù)期原值(A)和新值(B)。如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)西東將該位置值更新為新值。否則,處理器不做任何操作。
          記錄2: id,stauts,status 包含3種狀態(tài)值 1,2,3
          操作,update status=3 where id=111 and status=1;
          即 如果內(nèi)存值為1,預(yù)期值為1,則修改新值。對(duì)于沒有執(zhí)行的操作則丟棄。

          公平鎖與非公平鎖

          公平鎖與非公平鎖的直接的區(qū)別

          公平鎖: 就是比較公平,根據(jù)請(qǐng)求鎖的順序排列,先來請(qǐng)求先來請(qǐng)求的就先獲取鎖,后來獲取鎖就最 后獲取到, 采取隊(duì)列存放…類似類似于吃飯排隊(duì)。


          隊(duì)列---底層實(shí)現(xiàn)方式---數(shù)組或者鏈表實(shí)現(xiàn)


          非公平鎖:不是據(jù)請(qǐng)求的順序排列,通過爭(zhēng)搶的方式獲取鎖
          非公平鎖效率是公平鎖效率要高,Synchronized 是非公平鎖
          New ReentramtLock()(true)—公平鎖
          New ReentramtLock()(false)—非公平鎖
          底層基于 aqs 實(shí)現(xiàn)
          獨(dú)占鎖與共享鎖
          獨(dú)占鎖與共享鎖之間的區(qū)別
          獨(dú)占鎖: 在多線程中,只允許有一個(gè)線程獲取到鎖,其他線程都會(huì)等待。
          共享鎖: 多個(gè)線程可以同時(shí)持有鎖,
          例如 ReentrantLock 讀寫鎖。讀讀可以共享、寫寫互斥、讀寫互斥
          /**
           * 獨(dú)占鎖(寫鎖) 一次只能被一個(gè)線程占有
           * 共享鎖(讀鎖) 多個(gè)線程可以同時(shí)占有
           * ReadWriteLock
           * 讀-讀  可以共存!
           * 讀-寫  不能共存!
           * 寫-寫  不能共存!
           */
          public class ReadWriteLockDemo {
              public static void main(String[] args) {
                  MyCache myCache = new MyCache();
                  // 寫入
                  for (int i = 1; i <= 5; i++) {
                      final int temp = i;
                      new Thread(() -> {
                          myCache.put(temp + "", temp + "");
                      }, String.valueOf(i)).start();
                  }
                  // 讀取
                  for (int i = 1; i <= 5; i++) {
                      final int temp = i;
                      new Thread(() -> {
                          myCache.get(temp + "");
                      }, String.valueOf(i)).start();
                  }
              }
          }

          // 加鎖的
          class MyCacheLock {
              private volatile Map<String, Object> map = new HashMap<>();
              // 讀寫鎖: 更加細(xì)粒度的控制
              private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
              private Lock lock = new ReentrantLock();

              // 存,寫入的時(shí)候,只希望同時(shí)只有一個(gè)線程寫
              public void put(String key, Object value) {
                  readWriteLock.writeLock().lock();
                  try {
                      System.out.println(Thread.currentThread().getName() + "寫入" + key);
                      map.put(key, value);
                      System.out.println(Thread.currentThread().getName() + "寫入OK");
                  } catch (Exception e) {
                      e.printStackTrace();
                  } finally {
                      readWriteLock.writeLock().unlock();
                  }
              }

              // 取,讀,所有人都可以讀!
              public void get(String key) {
                  readWriteLock.readLock().lock();
                  try {
                      System.out.println(Thread.currentThread().getName() + "讀取" + key);
                      Object o = map.get(key);
                      System.out.println(Thread.currentThread().getName() + "讀取OK");
                  } catch (Exception e) {
                      e.printStackTrace();
                  } finally {
                      readWriteLock.readLock().unlock();
                  }
              }
          }

          /**
           * 自定義緩存
           */
          class MyCache {

              private volatile Map<String, Object> map = new HashMap<>();

              // 存,寫
              public void put(String key, Object value) {
                  System.out.println(Thread.currentThread().getName() + "寫入" + key);
                  map.put(key, value);
                  System.out.println(Thread.currentThread().getName() + "寫入OK");
              }

              // 取,讀
              public void get(String key) {
                  System.out.println(Thread.currentThread().getName() + "讀取" + key);
                  Object o = map.get(key);
                  System.out.println(Thread.currentThread().getName() + "讀取OK");
              }
          }


          可重入性

          在同一個(gè)線程中鎖可以不斷傳遞的,可以直接獲取Syn/lock ``aqs


          CAS(自旋鎖)

          CAS: 沒有獲得到鎖的線程是不會(huì)阻塞的,通過循環(huán)控制一直不斷的獲取鎖


          CAS:Compare and Swap,翻譯成比較并交換,執(zhí)行函數(shù)CAS(V,E,N)


          三個(gè)操作數(shù): 內(nèi)存值V, 舊的預(yù)期值E,要修改的新值N當(dāng)且僅當(dāng)預(yù)期值E和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為N,負(fù)責(zé)什么都不做


          (1)CAS是通過硬件指令,保證了原子性

          (2)java是通過unsafe jni技術(shù)

          原子類: AtomicBoolean,AtomicInteger,AtomicLong等使用 CAS 實(shí)現(xiàn)


          unsafe類



          優(yōu)點(diǎn):

          沒有獲取到鎖的資源,會(huì)一直子啊用戶態(tài),不會(huì)阻塞,沒有鎖的線程會(huì)一直通過循環(huán)控 制重試


          缺點(diǎn):

          通過死循環(huán)控制,消耗 cpu 資源比較高,需要控制循次數(shù),避免 cpu 飆高問題;


          Cas 本質(zhì)的原理:

          舊的預(yù)期值===>>>V(共享變量中值),才會(huì)修改我們的V


          基于 cas 實(shí)現(xiàn)鎖機(jī)制原理

          Cas 無鎖機(jī)制原理:

          • 定義一個(gè)鎖的狀態(tài)

          • 狀態(tài)狀態(tài)值=0,則表示沒有線程獲取到該鎖

          • 狀態(tài)狀態(tài)值=1,則表示有線程已經(jīng)持有該鎖

          實(shí)現(xiàn)細(xì)節(jié):

          CAS獲取鎖:

          將該鎖的狀態(tài)從0到1----能夠修改成功 cas 成功則表示獲取鎖成功

          如果獲取鎖失敗–修改失敗,則不會(huì)阻塞而是通過循環(huán)==(自旋控制重試)==


          CAS 釋放鎖:

          將該鎖的狀態(tài)從 1 改為 0 如果能夠改成功 cas 成功則表示釋放鎖成功。


          CAS 如何解決 ABA 的問題

          CAS主要檢查內(nèi)存值V 與舊的預(yù)值值=E是否一致,如果一致的情況下,則修改


          這時(shí)會(huì)存在ABA的問題:

          如果將原來的值A(chǔ),改為B,B有改為了A發(fā)現(xiàn)沒有發(fā)生變化,實(shí)際上已經(jīng)發(fā)生了變化,所以存在ABA問題


          解決方法,通過版本號(hào),對(duì)沒個(gè)變量更新的版本號(hào)+1


          引用原子引用,對(duì)應(yīng)的思想:樂觀鎖


          解決 aba 問題是否大:概念產(chǎn)生沖突,但是不影響結(jié)果,換一種方式 通過版本號(hào)碼方式

          package com.nie.juc.cas;/*
           *
           *@auth  wenzhao
           *@date 2021/4/25  17:19
           */

          import java.util.concurrent.TimeUnit;
          import java.util.concurrent.atomic.AtomicInteger;
          import java.util.concurrent.atomic.AtomicStampedReference;
          import java.util.concurrent.locks.Lock;
          import java.util.concurrent.locks.ReentrantLock;

          /**
           * 演示 aba 的問題
           * (1)第一個(gè)參數(shù) expectedReference:表示預(yù)期值。
           * (2)第二個(gè)參數(shù) newReference:表示要更新的值。
           * (3)第三個(gè)參數(shù) expectedStamp:表示預(yù)期的時(shí)間戳。
           * (4)第四個(gè)參數(shù) newStamp:表示要更新的時(shí)間戳。
           */
          public class CASDemo {

              //AtomicStampedReference 注意,如果泛型是一個(gè)包裝類,注意對(duì)象的引用問題
              // 注意:如果引用類型是 Long、Integer、Short、Byte、Character 一定一定要注意值的緩存區(qū)間!
              // 比如 Long、Integer、Short、Byte 緩存區(qū)間是在-128~127,
              // 會(huì)直接存在常量池中,而不在這個(gè)區(qū)間內(nèi)對(duì)象的值 則會(huì)每次都 new 一個(gè)對(duì)象,
              // 那么即使兩個(gè)對(duì)象的值相同,CAS 方法都會(huì)返回 false
              // 先聲明初始值,修改后的值和臨時(shí)的值是為了保證使用 CAS 方法不會(huì)因?yàn)閷?duì)象不一樣而返回 false


              // 正常在業(yè)務(wù)操作,這里面比較的都是一個(gè)個(gè)對(duì)象
              static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

              // CAS compareAndSet : 比較并交換!
              public static void main(String[] args) {
                  ;
                  new Thread(() -> {
                      int stamp = atomicStampedReference.getStamp(); // 獲得版本號(hào)
                      System.out.println("a1=>" + stamp);

                      try {
                          TimeUnit.SECONDS.sleep(1);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }

                      Lock lock = new ReentrantLock(true);

                      atomicStampedReference.compareAndSet(1, 2,
                              atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);

                      System.out.println("a2=>----" + atomicStampedReference.getStamp());


                      System.out.println("更改" + atomicStampedReference.compareAndSet(2, 1,
                              atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));

                      System.out.println("a3=>" + atomicStampedReference.getStamp());

                  }, "a").start();


                  // 樂觀鎖的原理相同!
                  new Thread(() -> {
                      int stamp = atomicStampedReference.getStamp(); // 獲得版本號(hào)
                      System.out.println("b1=>" + stamp);

                      try {
                          TimeUnit.SECONDS.sleep(2);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("+++++++++++++++++++++++++++++++++++++++++++++");
                      System.out.println(atomicStampedReference.compareAndSet(1, 6,
                             stamp, stamp + 1));

                      System.out.println("b2=>" + atomicStampedReference.getStamp());

                  }, "b").start();

              }
          }



          輸出:

          a1=>1
          b1=>1
          a2=>----2
          更改true
          a3=>3
          +++++++++++++++++++++++++++++++++++++++++++++
          false
          b2=>3


          利用原子類手寫 CAS 無鎖

          package com.nie.juc.cas;/*
           *
           *@auth  wenzhao
           *@date 2021/4/25  20:36
           */

          import java.util.concurrent.atomic.AtomicLong;
          import java.util.stream.IntStream;

          public class AtomicTryLock {
              private AtomicLong cas = new AtomicLong(0);
              private Thread lockCurrentThread;

              /**
               * 1 表示鎖已經(jīng)被獲取
               * 0 表示鎖沒有獲取
               * 利用 cas 將 0 改為 1 成功則表示獲取鎖
               *
               * @return
               */

              //加鎖
              private boolean tryLock() {
                  boolean result = cas.compareAndSet(0, 1);
                  if (result) {
                      lockCurrentThread = Thread.currentThread();
                  }
                  return result;
              }

              //釋放鎖
              private boolean unLock() {
                  if (lockCurrentThread != Thread.currentThread()) {
                      return false;
                  }
                  return cas.compareAndSet(0, 1);
              }

              public static void main(String[] args) {
                  AtomicTryLock atomicTryLock = new AtomicTryLock();
                  IntStream.range(1, 10).forEach((i) -> new Thread(() ->
                  {
                      try {
                          boolean result = atomicTryLock.tryLock();
                          if (result) {
                              atomicTryLock.lockCurrentThread = Thread.currentThread();
                              System.out.println(Thread.currentThread().getName() + ",獲取鎖成功~");
                          } else {
                              System.out.println(Thread.currentThread().getName() + ",獲取鎖失敗~");
                          }
                      } catch (Exception e) {
                      } finally {
                          //釋放鎖
                          if (atomicTryLock != null) {
                              atomicTryLock.unLock();
                          }
                      }
                  }).start());
              }

          }



          輸出

          Thread-0,獲取鎖成功~
          Thread-3,獲取鎖失敗~
          Thread-2,獲取鎖失敗~
          Thread-1,獲取鎖失敗~
          Thread-4,獲取鎖失敗~
          Thread-7,獲取鎖失敗~
          Thread-8,獲取鎖失敗~
          Thread-6,獲取鎖失敗~
          Thread-5,獲取鎖失敗~


          ————————————————

          版權(quán)聲明:本文為CSDN博主「面相薪水編程」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/qq_44236958/article/details/116138494





          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  亚洲电影在线2 | 麻豆色播| 婷婷97五月天 | 亚洲综合在线人妻 | 久热久热|