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

解析java中的condition

 更新時間:2021年06月11日 15:26:28   作者:bohu83  
Condition 將 Object 監(jiān)視器方法(wait()、notify()和notifyAll())分解成截然不同的對象,以便通過將這些對象與任意Lock實現(xiàn)組合使用,為每個對象提供多個等待 set(wait-set)

一、condition 介紹及demo

Condition是在java 1.5中才出現(xiàn)的,它用來替代傳統(tǒng)的Object的wait()、notify()實現(xiàn)線程間的協(xié)作,相比使用Object的wait()、notify(),使用Condition的await()、signal()這種方式實現(xiàn)線程間協(xié)作更加安全和高效。因此通常來說比較推薦使用Condition,阻塞隊列實際上是使用了Condition來模擬線程間協(xié)作。

  • Condition是個接口,基本的方法就是await()和signal()方法;
  • Condition依賴于Lock接口,生成一個Condition的基本代碼是lock.newCondition()   
  • 調(diào)用Condition的await()和signal()方法,都必須在lock保護之內(nèi),就是說必須在lock.lock()和lock.unlock之間才可以使用

Conditon中的await()對應(yīng)Object的wait();

Condition中的signal()對應(yīng)Object的notify();

Condition中的signalAll()對應(yīng)Object的notifyAll()。

condition常見例子arrayblockingqueue。下面是demo:

package thread;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConTest {
	
	 final Lock lock = new ReentrantLock();
	 final Condition condition = lock.newCondition();
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ConTest test = new ConTest();
	    Producer producer = test.new Producer();
	    Consumer consumer = test.new Consumer();
	          
	    
	    consumer.start(); 
	    producer.start();
	}
	
	 class Consumer extends Thread{
         
	        @Override
	        public void run() {
	            consume();
	        }
	          
	        private void consume() {
	           	             
	                try {
	                	   lock.lock();
	                    System.out.println("我在等一個新信號"+this.currentThread().getName());
	                    condition.await();
	                    
	                } catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally{
						System.out.println("拿到一個信號"+this.currentThread().getName());
	                    lock.unlock();
	                }
	            
	        }
	    }
	 
	 class Producer extends Thread{
         
	        @Override
	        public void run() {
	            produce();
	        }
	          
	        private void produce() {	             
	                try {
	                	   lock.lock();
	                       System.out.println("我拿到鎖"+this.currentThread().getName());
	                        condition.signalAll();	                         
	                    System.out.println("我發(fā)出了一個信號:"+this.currentThread().getName());
	                } finally{
	                    lock.unlock();
	                }
	            }
	 }
	    
}

運行結(jié)果:

Condition的執(zhí)行方式,是當(dāng)在線程Consumer中調(diào)用await方法后,線程Consumer將釋放鎖,并且將自己沉睡,等待喚醒,線程Producer獲取到鎖后,開始做事,完畢后,調(diào)用Condition的signalall方法,喚醒線程Consumer,線程Consumer恢復(fù)執(zhí)行。

以上說明Condition是一個多線程間協(xié)調(diào)通信的工具類,使得某個,或者某些線程一起等待某個條件(Condition),只有當(dāng)該條件具備( signal 或者 signalAll方法被帶調(diào)用)時 ,這些等待線程才會被喚醒,從而重新爭奪鎖。

Condition實現(xiàn)生產(chǎn)者、消費者模式:

package thread;

import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConTest2 {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
        
    public static void main(String[] args) throws InterruptedException  {
        ConTest2 test = new ConTest2();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();	          
        producer.start();
        consumer.start();
        Thread.sleep(0);
        producer.interrupt();
        consumer.interrupt();
    }
        
    class Consumer extends Thread{	          
        @Override
        public void run() {
            consume();
        }
        volatile boolean flag=true;  
        private void consume() {
            while(flag){
                lock.lock();
                try {
                    while(queue.isEmpty()){
                        try {
                            System.out.println("隊列空,等待數(shù)據(jù)");
                            notEmpty.await();
                        } catch (InterruptedException e) {	                          
                            flag =false;
                        }
                    }
                    queue.poll();                //每次移走隊首元素
                    notFull.signal();
                    System.out.println("從隊列取走一個元素,隊列剩余"+queue.size()+"個元素");
                } finally{
                    lock.unlock();
                }
            }
        }
    }
        
    class Producer extends Thread{	          
        @Override
        public void run() {
            produce();
        }
        volatile boolean flag=true;  
        private void produce() {
            while(flag){
                lock.lock();
                try {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("隊列滿,等待有空余空間");
                            notFull.await();
                        } catch (InterruptedException e) {
                            
                            flag =false;
                        }
                    }
                    queue.offer(1);        //每次插入一個元素
                    notEmpty.signal();
                    System.out.println("向隊列取中插入一個元素,隊列剩余空間:"+(queueSize-queue.size()));
                } finally{
                    lock.unlock();
                }
            }
        }
    }
}

運行結(jié)果如下:

二、Condition接口     

condition可以通俗的理解為條件隊列。當(dāng)一個線程在調(diào)用了await方法以后,直到線程等待的某個條件為真的時候才會被喚醒。這種方式為線程提供了更加簡單的等待/通知模式。Condition必須要配合鎖一起使用,因為對共享狀態(tài)變量的訪問發(fā)生在多線程環(huán)境下。一個Condition的實例必須與一個Lock綁定,因此Condition一般都是作為Lock的內(nèi)部實現(xiàn)。

await() :造成當(dāng)前線程在接到信號或被中斷之前一直處于等待狀態(tài)。

await(long time, TimeUnit unit) :造成當(dāng)前線程在接到信號、被中斷或到達指定等待時間之前一直處于等待狀態(tài)

awaitNanos(long nanosTimeout) :造成當(dāng)前線程在接到信號、被中斷或到達指定等待時間之前一直處于等待狀態(tài)。

返回值表示剩余時間,如果在nanosTimesout之前喚醒,那么返回值 = nanosTimeout - 消耗時間,如果返回值 <= 0 ,則可以認定它已經(jīng)超時了。

awaitUninterruptibly() :造成當(dāng)前線程在接到信號之前一直處于等待狀態(tài)?!咀⒁猓涸摲椒▽χ袛嗖幻舾小?。

awaitUntil(Date deadline) :造成當(dāng)前線程在接到信號、被中斷或到達指定最后期限之前一直處于等待狀態(tài)。如果沒有到指定時間就被通知,則返回true,否則表示到了指定時間,返回返回false。

signal() :喚醒一個等待線程。該線程從等待方法返回前必須獲得與Condition相關(guān)的鎖。

signal()All :喚醒所有等待線程。能夠從等待方法返回的線程必須獲得與Condition相關(guān)的鎖。

三、condition實現(xiàn)分析

  • Condition接口包含了多種await方式和兩個通知方法
  • ConditionObject實現(xiàn)了Condition接口,是AbstractQueuedSynchronizer的內(nèi)部類(因為Condition的操作都需要獲取想關(guān)聯(lián)的鎖)
  • Reentrantlock的newCondition方法返回與某個lock實例相關(guān)的Condition對象
public abstract class AbstractQueuedLongSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

結(jié)合上面的類圖,我們看到condition實現(xiàn)是依賴于aqs,而aqs是個抽象類。里面定義了同步器的基本框架,實現(xiàn)了基本的結(jié)構(gòu)功能。只留有狀態(tài)條件的維護由具體同步器根據(jù)具體場景來定制,如常見的 ReentrantLock 、 RetrantReadWriteLock和CountDownLatch 等等,

3.1、等待隊列

Condition是AQS的內(nèi)部類。每個Condition對象都包含一個隊列(等待隊列)。等待隊列是一個FIFO的隊列,在隊列中的每個節(jié)點都包含了一個線程引用,該線程就是在Condition對象上等待的線程,如果一個線程調(diào)用了Condition.await()方法,那么該線程將會釋放鎖、構(gòu)造成節(jié)點加入等待隊列并進入等待狀態(tài)。AQS有一個同步隊列和多個等待隊列,節(jié)點都是Node。等待隊列的基本結(jié)構(gòu)如下所示。

等待分為首節(jié)點和尾節(jié)點。當(dāng)一個線程調(diào)用Condition.await()方法,將會以當(dāng)前線程構(gòu)造節(jié)點,并將節(jié)點從尾部加入等待隊列。新增節(jié)點就是將尾部節(jié)點指向新增的節(jié)點。節(jié)點引用更新本來就是在獲取鎖以后的操作,所以不需要CAS保證。同時也是線程安全的操作。

public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;
    /** First node of condition queue. */
    private transient Node firstWaiter;
    /** Last node of condition queue. */
    private transient Node lastWaiter;

3.2、等待

 當(dāng)線程調(diào)用了Condition的await()方法以后。線程就作為隊列中的一個節(jié)點被加入到等待隊列中去了。同時會釋放鎖的擁有。當(dāng)從await方法返回的時候。當(dāng)前線程一定會獲取condition相關(guān)聯(lián)的鎖。

如果從隊列(同步隊列和等待隊列)的角度去看await()方法,當(dāng)調(diào)用await()方法時,相當(dāng)于同步隊列的首節(jié)點(獲取鎖的節(jié)點)移動到Condition的等待隊列中。

調(diào)用該方法的線程成功的獲取鎖的線程,也就是同步隊列的首節(jié)點,該方法會將當(dāng)前線程構(gòu)造成節(jié)點并加入到等待隊列中,然后釋放同步狀態(tài),喚醒同步隊列中的后繼節(jié)點,然后當(dāng)前線程會進入等待狀態(tài)。

當(dāng)?shù)却犃兄械墓?jié)點被喚醒的時候,則喚醒節(jié)點的線程開始嘗試獲取同步狀態(tài)。如果不是通過 其他線程調(diào)用Condition.signal()方法喚醒,而是對等待線程進行中斷,則會拋出InterruptedException異常信息。

   

我們看一下這個await的方法,它是AQS的方法,

public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter(); //將當(dāng)前線程包裝下后,
    //添加到Condition自己維護的一個鏈表中。
    int savedState = fullyRelease(node);//釋放當(dāng)前線程占有的鎖,從demo中看到,
    //調(diào)用await前,當(dāng)前線程是占有鎖的
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        //釋放完畢后,遍歷AQS的隊列,看當(dāng)前節(jié)點是否在隊列中,
        //不在 說明它還沒有競爭鎖的資格,所以繼續(xù)將自己沉睡。
        //直到它被加入到隊列中,聰明的你可能猜到了,
        //沒有錯,在singal的時候加入不就可以了?
    LockSupport.park(this);
    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
        break;
    }  
//被喚醒后,重新開始正式競爭鎖,同樣,如果競爭不到還是會將自己沉睡,等待喚醒重新開始競爭。
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
    unlinkCancelledWaiters();
if (interruptMode != 0)
    reportInterruptAfterWait(interruptMode);
}

結(jié)合代碼去看,同步隊列的首節(jié)點 并不會直接加入等待隊列,而是通過addConditionWaiter把當(dāng)前線程構(gòu)造成一個新節(jié)點并加入到等待隊列中。

/**
    * Adds a new waiter to wait queue.
    * @return its new wait node
    */
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

3.3、通知

 調(diào)用Condition的signal()方法,將會喚醒在等待隊列中等待最長時間的節(jié)點(條件隊列里的首節(jié)點),在喚醒節(jié)點前,會將節(jié)點移到同步隊列中。當(dāng)前線程加入到等待隊列中如圖所示:

回到上面的demo,鎖被釋放后,線程Consumer開始沉睡,這個時候線程因為線程Consumer沉睡時,會喚醒AQS隊列中的頭結(jié)點,所所以線程Producer會開始競爭鎖,并獲取到,執(zhí)行完后線程Producer會調(diào)用signal方法,“發(fā)出”signal信號,signal方法如下:

public final void signal() {
 if (!isHeldExclusively())
    throw new IllegalMonitorStateException();
 Node first = firstWaiter; //firstWaiter為condition自己維護的一個鏈表的頭結(jié)點,
 //取出第一個節(jié)點后開始喚醒操作
 if (first != null)
    doSignal(first);
}

在調(diào)用signal()方法之前必須先判斷是否獲取到了鎖(isHeldExclusively方法)。接著獲取等待隊列的首節(jié)點,將其移動到同步隊列并且利用LockSupport喚醒節(jié)點中的線程。

被喚醒的線程將從await方法中的while循環(huán)中退出(  while (!isOnSyncQueue(node)) { 方法返回true,節(jié)點已經(jīng)在同步隊列中)。隨后調(diào)用同步器的acquireQueued()方法加入到同步狀態(tài)的競爭當(dāng)中去。成功獲取到競爭的線程從先前調(diào)用await方法返回,此時該線程已經(jīng)成功獲取了鎖。

*********************************************

AQS的同步隊列與Condition的等待隊列,兩個隊列的作用是不同,事實上,每個線程也僅僅會同時存在以上兩個隊列中的一個,流程是這樣的:

注意:

1.線程producer調(diào)用signal方法,這個時候Condition的等待隊列中只有線程Consumer一個節(jié)點,于是它被取出來,并被加入到AQS的等待隊列中。  注意,這個時候,線程Consumer 并沒有被喚醒。

2.Sync是AQS的抽象子類,實現(xiàn)可重入和互斥的大部分功能。在Sync的子類中有FairSync和NonfairSync兩種代表公平鎖策略和非公平鎖策略。Sync lock方法留給子類去實現(xiàn),NonfairSync的實現(xiàn):

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

其中如果一開始獲取鎖成功,是直接設(shè)置當(dāng)前線程。

否則執(zhí)行acquire(1),也就是進入aqs等待隊列。這里不展開細節(jié)。

可以這樣理解,整個協(xié)作過程是靠結(jié)點在AQS的等待隊列和Condition的等待隊列中來回移動實現(xiàn)的,每個隊列的意義不同,Condition作為一個條件類,很好的自己維護了一個等待信號的隊列,并在適時的時候?qū)⒔Y(jié)點加入到AQS的等待隊列中來實現(xiàn)的喚醒操作

以上就是解析java中的condition的詳細內(nèi)容,更多關(guān)于java condition的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java多線程死鎖與資源限制操作

    Java多線程死鎖與資源限制操作

    這篇文章主要介紹了Java多線程死鎖與資源限制操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 使用Spring?Retry實現(xiàn)業(yè)務(wù)異常重試

    使用Spring?Retry實現(xiàn)業(yè)務(wù)異常重試

    在系統(tǒng)中經(jīng)常遇到業(yè)務(wù)重試的邏輯,比如三方接口調(diào)用,timeout重試三遍,異常處理重試的兜底邏輯等,本文給大家介紹一下如何使用Spring?Retry優(yōu)雅的實現(xiàn)業(yè)務(wù)異常重試,需要的朋友可以參考下
    2024-01-01
  • Java加載與存儲指令之ldc與_fast_aldc指令

    Java加載與存儲指令之ldc與_fast_aldc指令

    ldc指令將int、float、或者一個類、方法類型或方法句柄的符號引用、還可能是String型常量值從常量池中推送至棧頂。這一篇介紹一個虛擬機規(guī)范中定義的一個字節(jié)碼指令ldc,另外還有一個虛擬機內(nèi)部使用的字節(jié)碼指令_fast_aldc。需要的盆友可參考下面文章的內(nèi)容
    2021-09-09
  • idea注解參數(shù)換行時間日期格式設(shè)置方法

    idea注解參數(shù)換行時間日期格式設(shè)置方法

    這篇文章主要介紹了idea注解參數(shù)換行時間日期格式設(shè)置方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • redis?redisTemplate數(shù)據(jù)類型基礎(chǔ)操作

    redis?redisTemplate數(shù)據(jù)類型基礎(chǔ)操作

    這篇文章主要介紹了redis?redisTemplate數(shù)據(jù)類型基礎(chǔ)操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • springboot與springmvc基礎(chǔ)入門講解

    springboot與springmvc基礎(chǔ)入門講解

    本篇文章主要介紹了詳解快速搭建Spring Boot+Spring MVC,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • Java停止線程的3種方法

    Java停止線程的3種方法

    這篇文章主要分享Java停止線程的3種方法,分別是自定義中斷標(biāo)識符,停止線程、使用線程中斷方法interrupt停止線程、使用stop停止線程。下文詳細介紹需要的小伙伴可以參考一下
    2022-05-05
  • Java之一文詳解String字符串的用法

    Java之一文詳解String字符串的用法

    本文將給大家重點講解一下String的用法,因為這個太常用,也太??剂恕tring字符串的內(nèi)容是比較多的,需要初學(xué)者進行專門的學(xué)習(xí),尤其是它的一些底層原理更需要我們來了解,需要的同學(xué)跟著小編一起學(xué)習(xí)吧
    2023-05-05
  • mybatisplus開啟sql打印的三種方式匯總

    mybatisplus開啟sql打印的三種方式匯總

    這篇文章主要介紹了mybatisplus開啟sql打印的三種方式,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • java實現(xiàn)哈夫曼壓縮與解壓縮的方法

    java實現(xiàn)哈夫曼壓縮與解壓縮的方法

    這篇文章主要介紹了java實現(xiàn)哈夫曼壓縮與解壓縮的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10

最新評論

翼城县| 余庆县| 吉隆县| 辽阳县| 祁连县| 潢川县| 合川市| 将乐县| 密山市| 濮阳县| 隆德县| 探索| 饶河县| 平武县| 京山县| 庐江县| 建阳市| 楚雄市| 万全县| 外汇| 肃南| 景东| 志丹县| 巫溪县| 张家港市| 新巴尔虎右旗| 于都县| 聊城市| 张家川| 都江堰市| 天峻县| 南川市| 冀州市| 峨眉山市| 城市| 彝良县| 乌恰县| 石屏县| 筠连县| 云南省| 繁峙县|