<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>

          生產(chǎn)者消費(fèi)者模型ReetrantLock版本

          共 4485字,需瀏覽 9分鐘

           ·

          2021-09-21 19:32

          ????????不知道為什么,每次感覺多線程其實(shí)沒啥難的,但是一旦涉及到細(xì)節(jié)根本回答不出來,而且也不能很熟練的將多線程的代碼編寫出來。多線程只是表面理解起來簡單,就是有一些任務(wù),本來一個(gè)人執(zhí)行的,現(xiàn)在派大家伙一起來執(zhí)行他,執(zhí)行的過程中,大家排好隊(duì),記好數(shù),該你動(dòng)手的時(shí)候再動(dòng),不該你動(dòng)手的時(shí)候不要?jiǎng)印2还苁裁幢^鎖,樂觀鎖的,最后都是要按順序走的。唯一的不同就是一個(gè)是提前排好隊(duì),記錄好大家的信息,然后一個(gè)一個(gè)來領(lǐng)任務(wù)(這其中可能涉及到操作系統(tǒng)用戶態(tài)和和心態(tài)的切換,這個(gè)過程可能會(huì)比較費(fèi)時(shí))。另一個(gè)就是一堆人時(shí)不時(shí)的就上去問問現(xiàn)在能不能領(lǐng)任務(wù),不能領(lǐng)的話,我一會(huì)兒再來問問。最終就造就了一種共同的表象,我做完,你再做(其實(shí)就是線程同步這個(gè)概念)。


          ? ? ? ? 我打算通過不同版本的生產(chǎn)者消費(fèi)者模型來搞懂這里面的彎彎繞繞,這樣一來,我們才能不至于一直去死記硬背這種本來就很靈活的東西。我們需要實(shí)現(xiàn)的功能如下:


          有一家面包店,里面有多個(gè)烘培師,做完蛋糕放到一個(gè)大籃子里,然后外邊有一堆客人,需要吃了就去籃子里面取。整個(gè)過程要求,面包數(shù)量達(dá)到籃子容量就停止生產(chǎn)。籃子里面沒有面包了就停止消費(fèi)。同時(shí)生產(chǎn)的面包都有各自得編號(hào),一個(gè)面包只能被一個(gè)人生產(chǎn),被一個(gè)人消費(fèi)。



          ? ? ? ? 今天我們先從ReetrantLock入手,一開始我將生產(chǎn)者和消費(fèi)者公用一把鎖,因?yàn)榇蠹叶荚诓僮魍粋€(gè)List變量,但是我感覺消費(fèi)者消費(fèi)東西的快慢本來不應(yīng)該去干擾生產(chǎn)者生產(chǎn)東西的快慢。所以我最后選擇生產(chǎn)者自己使用一把鎖來同步生產(chǎn)者線程,消費(fèi)者使用一把鎖來同步消費(fèi)者線程。這里面的問題就是客戶每次使用size()方法得到的容量實(shí)際上不是真正的容量,但是好在不會(huì)影響到總數(shù),因?yàn)閟ize只有大于1才能進(jìn)行操作。



          烘焙師(生產(chǎn)者)


          @Slf4j
          public class Baker implements Runnable {
          //烘焙師姓名
          private String producerName;
          //放面包的籃子
          private volatile BreadBucket bucket = null;
          //保證烘焙師生產(chǎn)順序的鎖
          private static ReentrantLock bakerLock = new ReentrantLock();

          public Baker(String producerName, BreadBucket bucket){
          this.producerName = producerName;
          this.bucket = bucket;
          }

          /**
          * 生產(chǎn)方法
          */
          private void produce(){
          try{
          bakerLock.lock();
          if(bucket.container.size()<BreadBucket.capacity){
          System.out.println(producerName+"生產(chǎn)面包"+ ++BreadBucket.thingsNum);
          Thread.sleep(100);//生產(chǎn)面包所需時(shí)間0.1s
          Bread bread = new Bread("面包"+BreadBucket.thingsNum);
          bucket.putInto(bread);
          }
          }catch (Exception e){
          e.printStackTrace();
          }finally {
          bakerLock.unlock();
          }
          }

          @Override
          public void run() {
          while (true){
          produce();
          }
          }
          }



          客戶(消費(fèi)者)


          public class Client implements Runnable {

          //消費(fèi)者名稱
          private String name;
          //取面包的籃子
          private volatile BreadBucket breadBucket;
          //消費(fèi)者鎖
          private static ReentrantLock clientLock = new ReentrantLock();

          public Client(BreadBucket breadBucket, String name){
          this.name = name;
          this.breadBucket = breadBucket;
          }

          /**
          * 拿出面包
          * @return
          */
          private Bread takeOut(){
          Bread bread = null;
          try{
          clientLock.lock();
          if(breadBucket.container.size()>=1){//此處遇到問題了
          bread = breadBucket.takeOut();
          Thread.sleep(200);//消費(fèi)一個(gè)面包0.2秒鐘
          System.out.println(name+"消費(fèi)"+bread.getName()
          + "--剩余:"+breadBucket.getAllThing());
          }
          }catch (Exception e){
          e.printStackTrace();
          }finally {
          clientLock.unlock();
          }
          return bread;
          }

          @Override
          public void run() {
          while (true){
          takeOut();
          }
          }
          }


          烘焙師放面包和客戶取面包的籃子


          /**
          * 面包桶
          * @author Ted
          * @version 1.0
          * @date 2020/5/30 21:27
          */
          public class BreadBucket {

          public static Integer thingsNum = 0;

          public List<Bread> container = new ArrayList<>();

          public static final Integer capacity = 7;


          public void putInto(Bread bread){
          container.add(bread);
          }

          public Bread takeOut(){
          Random random = new Random();
          Bread t = container.get(random.nextInt()&(container.size()-1));
          container.remove(t);
          return t;
          }

          public List<Bread> getAllThing(){
          return container;
          }
          }


          面包店(main方法)


          /**
          * @author Ted
          * @version 1.0
          * @date 2020/5/31 9:47
          */
          public class Bakery {

          public static void main(String[] args) {
          BreadBucket breadBucket = new BreadBucket();
          Baker producer1 = new Baker("烘培師1號(hào)", breadBucket);
          Baker producer2 = new Baker("烘培師2號(hào)", breadBucket);
          Baker producer3 = new Baker("烘培師3號(hào)",breadBucket);
          Client consumer1 = new Client(breadBucket,"客戶1號(hào)");
          Client consumer2 = new Client(breadBucket,"客戶2號(hào)");
          Client consumer3 = new Client(breadBucket,"客戶3號(hào)");

          new Thread(consumer1).start();
          new Thread(consumer2).start();
          new Thread(consumer3).start();
          new Thread(producer1).start();
          new Thread(producer2).start();
          new Thread(producer3).start();

          }
          }


          結(jié)果如下


          caa831081d5ef9f17c75bf924059091d.webp


          小插曲


          ????????本來打算多做幾個(gè)版本,結(jié)果實(shí)際上這里面碰到個(gè)挺多的小問題。此處先標(biāo)記一下volatile的問題,當(dāng)不加這個(gè)關(guān)鍵字的時(shí)候,如果去掉消費(fèi)者中的鎖,會(huì)導(dǎo)致消費(fèi)者線程被系統(tǒng)掛起,然后生產(chǎn)者生產(chǎn)完第一批產(chǎn)品后,整個(gè)程序靜置在那里。但是給桶加上volatile關(guān)鍵字之后,去掉消費(fèi)者鎖,程序會(huì)一直執(zhí)行下去,當(dāng)然去掉鎖之后,程序會(huì)瘋狂報(bào)錯(cuò)。如果不加volatile關(guān)鍵字,但是給消費(fèi)者執(zhí)行體中加上一句System.out.println語句,會(huì)導(dǎo)致消費(fèi)者線程不會(huì)被掛起,會(huì)一直執(zhí)行下去。這個(gè)總感覺和JVM優(yōu)化有一定的聯(lián)系,導(dǎo)致了線程之間可見性的問題。一點(diǎn)一點(diǎn)搞懂吧!


          64a85074936aa5094f614a18c329b7a6.webp


          ????????加上System.out.println,會(huì)導(dǎo)致程序起碼會(huì)運(yùn)行起來,不會(huì)有種死循環(huán)的感覺。記錄下這個(gè)小問題。



          679139990faca24b8b3a90220caf0761.webp




          40f7d3ca0f748e2cfe7f1e4f0875f2ec.webp




          瀏覽 41
          點(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>
                  婷婷五月天黄色电影 | 日逼网站免费看 | 国产激情精品无码 | 成人做爰黄 片视频动漫 | 91丨国产丨豆花 |