最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java并發(fā)等待條件的實(shí)現(xiàn)原理詳解

 更新時(shí)間:2017年11月09日 10:59:32   作者:yanyan19880509  
這篇文章主要介紹了java并發(fā)等待條件的實(shí)現(xiàn)原理詳解,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。

前言

前面介紹了排它鎖,共享鎖的實(shí)現(xiàn)機(jī)制,本篇繼續(xù)學(xué)習(xí)AQS中的另外一個(gè)內(nèi)容-Condition。想必學(xué)過java的都知道Object.wait和Object.notify,同時(shí)也應(yīng)該知曉這兩個(gè)方法的使用離不開synchronized關(guān)鍵字。synchronized是jvm級(jí)別提供的同步原語,它的實(shí)現(xiàn)機(jī)制隱藏在jvm實(shí)現(xiàn)中。作為Lock系列功能中的Condition,就是用來實(shí)現(xiàn)類似 Object.wait和Object.notify 對(duì)應(yīng)功能的。

使用場景

為了更好的理解Lock和Condition的使用場景,下面我們先來實(shí)現(xiàn)這樣一個(gè)功能:有多個(gè)生產(chǎn)者,多個(gè)消費(fèi)者,一個(gè)產(chǎn)品容器,我們假設(shè)容器最多可以放3個(gè)產(chǎn)品,如果滿了,生產(chǎn)者需要等待產(chǎn)品被消費(fèi),如果沒有產(chǎn)品了,消費(fèi)者需要等待。我們的目標(biāo)是一共生產(chǎn)10個(gè)產(chǎn)品,最終消費(fèi)10個(gè)產(chǎn)品,如何在多線程環(huán)境下完成這一挑戰(zhàn)呢?下面是我簡單實(shí)現(xiàn)的一個(gè)demo,僅供參考。

package com.lock.condition.test;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class LockConditionTest {
  // 生產(chǎn) 和 消費(fèi) 的最大總數(shù)
  public static int totalCount = 10;
  // 已經(jīng)生產(chǎn)的產(chǎn)品數(shù)
  public static volatile int hasProduceCount = 0;
  // 已經(jīng)消費(fèi)的產(chǎn)品數(shù)
  public static volatile int hasConsumeCount = 0;
  // 容器最大容量
  public static int containerSize = 3;
  // 使用公平策略的可重入鎖,便于觀察演示結(jié)果
  public static ReentrantLock lock = new ReentrantLock(true);
  public static Condition notEmpty = lock.newCondition();
  public static Condition notFull = lock.newCondition();
  // 容器
  public static LinkedList<integer> container = new LinkedList<integer>();
  // 用于標(biāo)識(shí)產(chǎn)品
  public static AtomicInteger idGenerator = new AtomicInteger();
  public static void main(String[] args) {
    Thread p1 = new Thread(new Producer(), "p-1");
    Thread p2 = new Thread(new Producer(), "p-2");
    Thread p3 = new Thread(new Producer(), "p-3");
    Thread c1 = new Thread(new Consumer(), "c-1");
    Thread c2 = new Thread(new Consumer(), "c-2");
    Thread c3 = new Thread(new Consumer(), "c-3");
    c1.start();
    c2.start();
    c3.start();
    p1.start();
    p2.start();
    p3.start();
    try{
      c1.join();
      c2.join();
      c3.join();
      p1.join();
      p2.join();
      p3.join();
    }catch(Exception e){
    }
    System.out.println(" done. ");
  }
  static class Producer implements Runnable{
    @Override
    public void run() {
      while(true){
        lock.lock();
        try{
          // 容器滿了,需要等待非滿條件
          while(container.size() >= containerSize){
            notFull.await();
          }
          // 到這里表明容器未滿,但需要再次判斷是否已經(jīng)完成了任務(wù)
          if(hasProduceCount >= totalCount){
            System.out.println(Thread.currentThread().getName()+" producer exit");
            return ;
          }
          int product = idGenerator.incrementAndGet();
          // 把生產(chǎn)出來的產(chǎn)品放入容器
          container.addLast(product);
          System.out.println(Thread.currentThread().getName() + " product " + product);
          hasProduceCount++;
          // 通知消費(fèi)線程可以去消費(fèi)了
          notEmpty.signal();
        } catch (InterruptedException e) {
        }finally{
          lock.unlock();
        }
      }
    }
  }
  static class Consumer implements Runnable{
    @Override
    public void run() {
      while(true){
        lock.lock();
        try{
          if(hasConsumeCount >= totalCount){
            System.out.println(Thread.currentThread().getName()+" consumer exit");
            return ;
          }
          // 一直等待有產(chǎn)品了,再繼續(xù)往下消費(fèi)
          while(container.isEmpty()){
            notEmpty.await(2, TimeUnit.SECONDS);
            if(hasConsumeCount >= totalCount){
              System.out.println(Thread.currentThread().getName()+" consumer exit");
              return ;
            }
          }
          Integer product = container.removeFirst();
          System.out.println(Thread.currentThread().getName() + " consume " + product);
          hasConsumeCount++;
          // 通知生產(chǎn)線程可以繼續(xù)生產(chǎn)產(chǎn)品了
          notFull.signal();
        } catch (InterruptedException e) {
        }finally{
          lock.unlock();
        }
      }
    }
  }
}

一次執(zhí)行結(jié)果如下:

p-1 product 1
p-3 product 2
p-2 product 3
c-3 consume 1
c-2 consume 2
c-1 consume 3
p-1 product 4
p-3 product 5
p-2 product 6
c-3 consume 4
c-2 consume 5
c-1 consume 6
p-1 product 7
p-3 product 8
p-2 product 9
c-3 consume 7
c-2 consume 8
c-1 consume 9
p-1 product 10
p-3 producer exit
p-2 producer exit
c-3 consume 10
c-2 consumer exit
c-1 consumer exit
p-1 producer exit
c-3 consumer exit
 done.

從結(jié)果可以發(fā)現(xiàn)已經(jīng)達(dá)到我們的目的了。

深入理解Condition的實(shí)現(xiàn)原理

上面的示例只是為了展示 Lock結(jié)合Condition可以實(shí)現(xiàn)的一種經(jīng)典場景,在有了感性的認(rèn)識(shí)之后,我們將一步一步來觀察Lock和Condition是如何協(xié)作完成這一任務(wù)的,這也是本篇的核心內(nèi)容。

為了更好的理解和演示這一個(gè)過程,我們使用到的鎖是使用公平策略模式的,我們會(huì)使用上面例子運(yùn)作的流程。我們會(huì)使用到3個(gè)生產(chǎn)線程,3個(gè)消費(fèi)線程,分別表示 p1、p2、p3和c1、c2、c3。

Condition的內(nèi)部實(shí)現(xiàn)是使用節(jié)點(diǎn)鏈來實(shí)現(xiàn)的,每個(gè)條件實(shí)例對(duì)應(yīng)一個(gè)節(jié)點(diǎn)鏈,我們有notEmpty 和 notFull 兩個(gè)條件實(shí)例,所以會(huì)有兩個(gè)等待節(jié)點(diǎn)鏈。

一切準(zhǔn)備就緒 ,開始我們的探索之旅。

1、線程c3執(zhí)行,然后發(fā)現(xiàn)沒有產(chǎn)品可以消費(fèi),執(zhí)行 notEmpty.await,進(jìn)入等待隊(duì)列中等候。

2、線程c2和線程c1執(zhí)行,然后發(fā)現(xiàn)沒有產(chǎn)品可以消費(fèi),執(zhí)行 notEmpty.await,進(jìn)入等待隊(duì)列中等候。

3、 線程 p1 啟動(dòng),得到了鎖,p1開始生產(chǎn)產(chǎn)品,這時(shí)候p3搶在p2之前,執(zhí)行了lock操作,結(jié)果p2和p3都處于等待狀態(tài),入同步隊(duì)列等待。<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這里寫圖片描述" src="/uploadfile/Collfiles/20160912/20160912092710536.png" title="\" />

注意,本例中我們使用的是公平策略模式下的排它鎖,由于p3搶先執(zhí)行取鎖操作,所以雖然p2和p3都被阻塞了,但是p3會(huì)優(yōu)先被喚醒 。

4、這會(huì),p1生產(chǎn)完畢,通知 not empty等待隊(duì)列,可以喚醒一個(gè)等待線程節(jié)點(diǎn)了,然后釋放了鎖,釋放鎖會(huì)導(dǎo)致p3被喚醒,然后p1進(jìn)入下一個(gè)循環(huán),進(jìn)入同步隊(duì)列。

事情開始變得有趣了,p1執(zhí)行一次生產(chǎn)后,執(zhí)行了 notEmpty.signal,其效果就是把 not empty等待列表中的頭節(jié)點(diǎn),即c3節(jié)點(diǎn)移到同步等待列隊(duì)中,重新參與搶占鎖。

5、p3生產(chǎn)完了產(chǎn)品后,繼續(xù)notEmpty.signal,同時(shí)釋放鎖,釋放鎖后會(huì)喚醒p2線程,然后p3在下一輪嘗試獲取鎖的時(shí)候,再次入隊(duì)。

6、接著,p2繼續(xù)生產(chǎn),生產(chǎn)后執(zhí)行 notEmpty.signal,同時(shí)釋放鎖,釋放鎖后喚醒c3線程,然后p2在下一輪嘗試取鎖的時(shí)候,入列。

7、c3進(jìn)行消費(fèi),你可以看到,現(xiàn)在 not empty等待列隊(duì)中已經(jīng)沒有等待節(jié)點(diǎn)了,由于我們使用的是公平策略排它鎖,這就會(huì)導(dǎo)致同步隊(duì)列中的節(jié)點(diǎn)一個(gè)接著一個(gè)執(zhí)行,而目前同步隊(duì)列中的節(jié)點(diǎn)排列為一生產(chǎn),一消費(fèi),這不難可以知道,接下來代碼已經(jīng)不會(huì)進(jìn)入 wait條件了,所以一個(gè)一個(gè)輪流執(zhí)行就是,比如c3,執(zhí)行完了,繼續(xù)notFull.signal(); 然后釋放鎖,入隊(duì),這里要明白,notFull.signal();這句代碼其實(shí)沒有作用了,因?yàn)?not full等待隊(duì)列中沒有任何等待線程節(jié)點(diǎn)。 c3執(zhí)行后,狀態(tài)如下圖所示:

8、后面的事情我想大家都可以想得出來是怎樣一步一步交替執(zhí)行的了。

總結(jié)

本篇基于一個(gè)實(shí)例來演示結(jié)合Lock和Condition如何實(shí)現(xiàn)生產(chǎn)-消費(fèi)模式,而且只討論一種可能執(zhí)行的流程,是想更簡單的表述AQS底層是如何實(shí)現(xiàn)的?;谏厦孢@個(gè)演示過程,針對(duì)其它的執(zhí)行流程,其原來也是一樣的。Condition內(nèi)部使用一個(gè)節(jié)點(diǎn)鏈來保存所有 wait狀態(tài)的線程,當(dāng)對(duì)應(yīng)條件被signal的時(shí)候,就會(huì)把等待節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中,繼續(xù)競爭鎖。原理其實(shí)并不復(fù)雜,有興趣的朋友可以翻閱源碼。

以上就是本文關(guān)于java并發(fā)等待條件的實(shí)現(xiàn)原理詳解的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:java并發(fā)學(xué)習(xí)之BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者詳解、Java并發(fā)之嵌套管程鎖死詳解、Java系統(tǒng)的高并發(fā)解決方法詳解等,有什么問題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。感謝朋友們對(duì)本站的支持!

相關(guān)文章

  • 詳解Java 10 var關(guān)鍵字和示例教程

    詳解Java 10 var關(guān)鍵字和示例教程

    在本文中,我將通過示例介紹新的Java SE 10特性——“var”類型。你將學(xué)習(xí)如何在代碼中正確使用它,以及在什么情況下不能使用它,需要的朋友可以參考下
    2018-10-10
  • Java快速排序案例講解

    Java快速排序案例講解

    這篇文章主要介紹了Java快速排序案例講解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • JAVA基礎(chǔ)面試題整理

    JAVA基礎(chǔ)面試題整理

    在本篇文章里小編給大家整理的是關(guān)于JAVA基礎(chǔ)面試題的整理內(nèi)容,需要的朋友們可以參考下。
    2019-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之有效隊(duì)列定義與用法示例

    Java數(shù)據(jù)結(jié)構(gòu)之有效隊(duì)列定義與用法示例

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之有效隊(duì)列定義與用法,結(jié)合實(shí)例形式分析了java有效隊(duì)列的數(shù)據(jù)插入、刪除、判斷、計(jì)算等相關(guān)操作技巧,需要的朋友可以參考下
    2017-10-10
  • java中set接口使用方法詳解

    java中set接口使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了java中set接口使用方法,介紹了Java中Set集合接口實(shí)現(xiàn)插入對(duì)象不重復(fù)的原理,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 通過Java讀取xml文件內(nèi)容過程解析

    通過Java讀取xml文件內(nèi)容過程解析

    這篇文章主要介紹了通過Java讀取xml文件內(nèi)容過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • JavaWeb實(shí)現(xiàn)簡單的自動(dòng)登錄功能

    JavaWeb實(shí)現(xiàn)簡單的自動(dòng)登錄功能

    這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)簡單的自動(dòng)登錄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java中的代理模式詳解及實(shí)例代碼

    Java中的代理模式詳解及實(shí)例代碼

    這篇文章主要介紹了Java中的代理模式詳解及實(shí)例代碼的相關(guān)資料,這里附有實(shí)例代碼,需要的朋友可以參考下
    2017-02-02
  • SpringBoot手寫自定義starter源碼

    SpringBoot手寫自定義starter源碼

    這篇文章主要介紹了SpringBoot手寫自定義starter源碼,SpringBoot擁有很多方便使用的starter,比如spring-boot-starter-log4j、mybatis-spring-boot-starter.jar等,各自都代表了一個(gè)相對(duì)完整的功能模塊,需要的朋友可以參考下
    2023-10-10
  • Java Map集合詳解與演示

    Java Map集合詳解與演示

    Map用于保存具有映射關(guān)系的數(shù)據(jù),Map集合里保存著兩組值,一組用于保存Map的ley,另一組保存著Map的value,可以理解為Map中的元素是兩個(gè)對(duì)象,一個(gè)對(duì)象作為鍵,一個(gè)對(duì)象作為值。鍵不可以重復(fù),但是值可以重復(fù)
    2021-11-11

最新評(píng)論

丽水市| 谢通门县| 集安市| 永昌县| 沁阳市| 丰宁| 阿鲁科尔沁旗| 巴里| 兴和县| 山阳县| 邹平县| 若尔盖县| 东兰县| 荆门市| 祁连县| 博客| 阜康市| 临泽县| 若羌县| 淮阳县| 鱼台县| 灌南县| 黎川县| 临安市| 泸西县| 和政县| 永安市| 贡山| 南丹县| 隆德县| 济源市| 牙克石市| 梅州市| 永和县| 株洲市| 泾源县| 永丰县| 六枝特区| 大连市| 安达市| 甘孜县|