精华内容
下载资源
问答
  • Python中使用threading.Condition交替打印两个字符的程序。 这个程序涉及到两个线程的的协调问题,两个线程为了能够相互协调运行,必须持有一个共同的状态,通过这个状态来维护两个线程的执行,通过使用threading....
  • 主要介绍了Spring实战之缓存使用condition操作,结合实例形式分析了Spring缓存使用condition具体配置、属性、领域模型等相关操作技巧与注意事项,需要的朋友可以参考下
  • Condition是java.util.concurrent.locks包下的类,提供了对线程锁的更精细的控制方法,下面我们就来看一下Java多线程编程中使用Condition类操作锁的方法详解
  • 通过@Bean和@Condition 注解自定义对于的condition里面根据自定义的条件实现指定类注入到spring中;@ConditionalOnProperty可以根据配置文件中的 属性值不同将不同的类注入到spring中 该资源中案例完整,代码简单移动
  • 主要介绍了java多线程加锁以及Condition类的使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 但是现有的方法往往将其视作情感分类任务,没有对目标话题和微博文本之间的关系特征进行分析,在基于深度学习的分类框架上,扩展并提出了基于Bert-Condition-CNN的立场检测模型,首先为提高话题在文本中的覆盖率,对...
  • MyBatisPlus条件构造器Condition的用法示例代码
  • Condition

    千次阅读 2018-02-15 17:49:39
    在上一篇博客《生产者消费者模式之Lock与Condition》中已经对Condition的使用有所了解了,下面再举一个之前在网上看过的例子作为Condition的补充。 问题:假设有三个线程,一个主线程mainThread和两个子线程...

           在上一篇博客《生产者消费者模式之LockCondition中已经对Condition的使用有所了解了,下面再举一个之前在网上看过的例子作为Condition的补充。

     

        问题:假设有三个线程,一个主线程mainThread和两个子线程subThread1、subThread2,要求按顺序输出,mainThread先输出1——3,然后subThread1输出4——6,最后subThread2输出7——9,循环执行。


        问题分析:有三个线程要顺序执行,所以我们可以定义三个Condition分别表示这三个线程,然后定义一个判断标记flagflag==0表示mainThreadflag==1表示subThread1flag==2表示subThread2。


    package com.gk.thread.condition.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    
    public class ConditionDemo {
    	
    	private static int count = 1;
    	
    	private Lock lock = new ReentrantLock();
    	private Condition mainCondition = lock.newCondition();
    	private Condition subCondition1 = lock.newCondition();
    	private Condition subCondition2 = lock.newCondition();
    	
    	/*
    	 * flag = 0代表mainCondition
    	 * flag = 1代表subCondition1
    	 * flag = 2代表subCondition2
    	 */
    	private int flag = 0;
    	
    	/**
    	 * mainThread线程调的方法
    	 */
    	public void mainThread() {
    			
    		lock.lock();
    		try {
    			while(flag != 0) {	
    				mainCondition.await();	// 如果flag != 0,mainThread线程等待
    			}
    			
    			// 如果flag == 0,mainThread线程往下执行
    			System.out.println("第" + count++ + "次输出");
    			for(int i=1; i<=3; i++) {
    				System.out.println("mainThread : " + i);
    			}
    			System.out.println();
    			
    			// 修改flag = 1,并唤醒subCondition1执行
    			flag = 1;		
    			subCondition1.signal();
    			
    		} catch (InterruptedException e) {
    			throw new RuntimeException(e);
    		}finally {
    			
    			lock.unlock();
    		}
    	}
    		
    	/**
    	 * subThread1线程调的方法
    	 */
    	public void subThread1() {
    			
    		lock.lock();
    			
    		try {
    			while(flag != 1) {
    				subCondition1.await();
    			}
    			
    			for(int i=4; i<=6; i++) {
    				System.out.println("subThread1 : " + i);
    			}
    			System.out.println();
    			
    			flag = 2;
    			subCondition2.signal();
    				
    		} catch (InterruptedException e) {
    			throw new RuntimeException(e);
    		}finally {
    			lock.unlock();
    		}
    	}
    
    	/**
    	 * subThread2线程调的方法
    	 */
    	public void subThread2() {
    			
    		lock.lock();
    			
    		try {
    			while(flag != 2) {
    				subCondition2.await();
    			}
    				
    			for(int i=7; i<=9; i++) {
    				System.out.println("subThread2 : " + i);
    			}
    			
    			System.out.println("\n=======================\n");
    			flag = 0;
    			mainCondition.signal();
    		} catch (InterruptedException e) {
    			throw new RuntimeException(e);
    		}finally {
    			lock.unlock();
    		}
    	
    	}
    		
    }


        测试代码:


    package com.gk.thread.condition.demo;
    
    public class Test {
    	
    	public static void main(String[] args) {
    		
    		final int COUNT = 3;
    		ConditionDemo cd = new ConditionDemo();
    		
    		for (int i=1; i<=COUNT; i++) {
    			
    			new Thread(new Runnable() {
    				
    				@Override
    				public void run() {
    					
    					cd.mainThread();	// mainThread线程
    				}	
    			}).start();
    			
    			new Thread(new Runnable() {
    				
    				@Override
    				public void run() {
    					
    					cd.subThread1();	// subThread1线程
    				}
    			}).start();
    
    			new Thread(new Runnable() {
    				
    				@Override
    				public void run() {
    					
    					cd.subThread2();	// subThread2线程
    				}
    			}).start();
    			
    		}
    	}
    
    }


        最后再友情提示一句:在API中有Condition实现阻塞队列的示例,读者可以体会其中算法思想。



     

        

    展开全文
  • 本文实例讲述了python多线程高级锁condition简单用法。分享给大家供大家参考,具体如下: 多线程编程中如果使用Condition对象代替lock, 能够实现在某个事件触发后才处理数据, condition中含有的方法: – wait:线程...
  • Condition.empty().when(false).set("1").orWhenEquals("1").toDo(p -> System.out.println("1")); Condition.of(1).whenEquals(1).toDo(p -> System.out.println("2")).elseDo(System.out::print); Condition.of...
  • 主要介绍了Java并发编程之Condition源码分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了Python线程条件变量Condition原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • var SQLConditionBuilder = require('sql-condition-builder'); var builder = new SQLConditionBuilder(); var obj = {a:'Hello',b:'!World'} var condition = builder.build(obj); // ->"a = 'Hello
  • 下面小编就为大家带来一篇类似Object监视器方法的Condition接口(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 领会下面这个示例吧,其实跟java中wait/nofity是一样一样的道理 import threading # 条件变量,用于复杂的线程间同步锁 """ 需求: 男:小姐姐,你好呀! 女:哼,想泡老娘不成?... self.condition = condition
  • 主要介绍了Java编程中实现Condition控制线程通信,简单介绍了Java中控制线程通信的方法,以及对condition的解析和实例,具有一定参考价值,需要的朋友可以了解下。
  • 主要介绍了Java多线程中ReentrantLock与Condition详解,需要的朋友可以参考下
  • 详细介绍了线程同步条件变量condition_variable的使用和它的源码,涉及到unique_lock, mutex, lock_guard, 虚假唤醒和惊群效应。
  • Condition详解

    2020-07-22 14:09:21
    Condition详解 传统上,我们可以同个synchronized关键字 + wait + notify/notifyAll 来实现多个线程之间的协调与通信,整个过程都是由JVM来帮助我们实现的;开发者无需(也无法)了解底层的细节。 从JDK 1.5开始,并...

    Condition详解

    传统上,我们可以通过synchronized关键字 + wait + notify/notifyAll 来实现多个线程之间的协调与通信,整个过程都是由JVM来帮助我们实现的;开发者无需(也无法)了解底层的细节。

    从JDK 1.5开始,并发包提供了Lock,Condition(await与signal/signalAll)来实现多个线程之间的协调与通信,整个过程都是开发者来控制的,而且相比于传统方式,更加灵活,功能也更加强大。

    Condition实际上就是将monitor的方法(wait、notify and notifyAll)放置到了各种各样的对象中,实现了一个对象拥有多个等待集合,通过使用任意一个Lock来实现将它们组合起来,用Lock来替换掉synchronized方法或块,Condition来替换掉对象monitor的方法。
    Condition(condition队列或者condition变量)提供一种方式:可以让一个线程挂起执行,直到当某一个状态的条件变成了true,被另外一个线程通知。由于对共享的状态信息的访问是发生在不同的线程当中的,因此它必须受到保护,这样某种形式的lock就会关联到condition上。这个关键的属性就是等待一个条件,它会自动的释放于condition关联的锁,并且挂起当前线程,相当于Object.wait。
    一个Condition天然的会被绑定到Lock上。我们要想获得一个特定的Lock实例所获得的一个Condition实例的话,就需要使用它的newCondition()放到。
    Condition实现所提供的行为和语义可以和Object的monitor的方法是不一样的,比如说对于通知的确定性排序,或者在执行通知的时候不要求一定要持有这个锁。如果某个实现提供了这些专门化的语义,对于实现来说,必须在文档中详细的记录下来。有一定要避免这种混淆,永远不应该用这种方式去使用Condition,除非是在它们的内部实现当中。
    注意,Condition实例仅仅就是普通对象,可以被用作synchronized代码块的目标,而且它们拥有自己的监视器的wait和notification方法也会被执行。获取到一个Condition实例的monitor锁,或者使用它的monitor方法,与获取Lock和Lock相关联的Condition以及使用它们的wating和signalling方法是没有任何关系的。

    Condition应该总是在一个循环当中等待,排查假唤醒的发生。

       class BoundedBuffer {
         final Lock lock = new ReentrantLock();
         final Condition notFull  = lock.newCondition(); 
         final Condition notEmpty = lock.newCondition(); 
      
         final Object[] items = new Object[100];
         int putptr, takeptr, count;
      
         public void put(Object x) throws InterruptedException {
           lock.lock();
           try {
             while (count == items.length)
               notFull.await();
             items[putptr] = x;
             if (++putptr == items.length) putptr = 0;
             ++count;
             notEmpty.signal();
           } finally {
             lock.unlock();
           }
         }
      
         public Object take() throws InterruptedException {
           lock.lock();
           try {
             while (count == 0)
               notEmpty.await();
             Object x = items[takeptr];
             if (++takeptr == items.length) takeptr = 0;
             --count;
             notFull.signal();
             return x;
           } finally {
             lock.unlock();
           }
         }
       }
    

    Condition newCondition()返回一个绑定这个Lock实例的Condition实例。
    在等待条件发生之前,当前线程要持有这把锁。对于Condition.await()的调用将会在等待之前自动释放锁,并且在wait方法返回之前尝试重新获取到这把锁。

    Condition提供的方法

        void await() throws InterruptedException;
        void awaitUninterruptibly();
        long awaitNanos(long nanosTimeout) throws InterruptedException;
        boolean await(long time, TimeUnit unit) throws InterruptedException;
        boolean awaitUntil(Date deadline) throws InterruptedException;
        void signal();
        void signalAll();    
    

    await()方法

    await方法会导致线程等待,直到被通知(另外一个线程调用了signal/signalAll)或者被中断。
    当我们调用了Condition的await方法之后,与Condition所关联的锁将会被当前线程自动释放,然后,当前线程将无法进行调度直到以下四种情况发生:

    • 其他线程调用了这个Condition的signal方法,而且当前线程恰巧被选为要被唤醒的线程。
    • 其他线程调用了这个Condition的signalAll方法。
    • 其他线程中断了当前线程,而且支持线程中断的挂起。
    • 出现“假唤醒”。
      以上所有情况,在await能返回之前,当前线程必须重新获取到与这个condition关联的锁。当线程返回的时候,确保线程持有这把锁。

    awaitUninterruptibly()方法

    awaitUninterruptibly方法会导致线程等待,直到被通知(另外一个线程调用了signal/signalAll)。
    当我们调用了Condition的awaitUninterruptibly方法之后,与Condition所关联的锁将会被当前线程自动释放,然后,当前线程将无法进行调度直到以下三种情况发生:

    • 其他线程调用了这个Condition的signal方法,而且当前线程恰巧被选为要被唤醒的线程。
    • 其他线程调用了这个Condition的signalAll方法。
    • 出现“假唤醒”。

    awaitNanos(long)方法&&boolean await(long time, TimeUnit unit)

    awaitNanos方法会导致线程等待,直到被通知(另外一个线程调用了signal/signalAll)或者被中断或者指定的等待时间已经过去。
    当我们调用了Condition的awaitNanos方法之后,与Condition所关联的锁将会被当前线程自动释放,然后,当前线程将无法进行调度直到以下四种情况发生:

    • 其他线程调用了这个Condition的signal方法,而且当前线程恰巧被选为要被唤醒的线程。
    • 其他线程调用了这个Condition的signalAll方法。
    • 其他线程中断了当前线程,而且支持线程中断的挂起。
    • 指定的等待时间已经过去,线程仍然没有被调度,处于等待状态。
    • 出现“假唤醒”。

    awaitNanos方法会返回一个给定超时时间剩下的时间的近似纳秒数(超时时间-等待时间),如果超时还有可能返回一个小于或者等于0的值。这个值可以用来去决定是否以及多长时间需要重新进行等待。

           boolean aMethod(long timeout, TimeUnit unit) {
            long nanos = unit.toNanos(timeout);
            lock.lock();
            try {
              while (!conditionBeingWaitedFor()) {
                if (nanos <= 0L)
                  return false;
                nanos = theCondition.awaitNanos(nanos);
              }
              // ...
            } finally {
              lock.unlock();
            }
          }
    

    设计原则,这个方法需要一个纳秒的参数来避免去返回剩余时间结论上的错误。这种精度的损失可能对于程序员来说是非常困难的,用它来确保整体的等待时间。(时间不是完全精准的)

    boolean await(long time, TimeUnit unit) throws InterruptedException;
    等价于
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    awaitUntil(Date deadline)

    直到未来的某个时间点的时候,如果线程还在等待,则返回false,否则返回true。

           boolean aMethod(Date deadline) {
            boolean stillWaiting = true;
            lock.lock();
            try {
              while (!conditionBeingWaitedFor()) {
                if (!stillWaiting)
                  return false;
                stillWaiting = theCondition.awaitUntil(deadline);
              }
              // ...
            } finally {
              lock.unlock();
            }
          }
    

    signal()方法

    唤醒一个等待的线程。
    如果在等待的condition上有多个线程在等待则唤醒其中的一个(底层通过单向链表实现,唤醒第一个节点)。这个线程在await返回之前必须重新获取到这把锁。

    signalAll()方法

    唤醒所有处于等待状态的线程。
    如果有个线程在condition上等待,它们都会被唤醒。每个线程也要重新获取到锁之后才能从await返回。

    package com.learn.thread.condition;
    
    import java.util.Arrays;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.stream.IntStream;
    
    public class ConditionTest2 {
    
        public static void main(String[] args) {
            BoundedContainer bc = new BoundedContainer();
    
            IntStream.range(0, 15).forEach(i -> new Thread(() -> {
                System.out.println(bc.take());
            }).start());
    
            IntStream.range(0, 15).forEach(i -> new Thread(() -> {
                bc.put("元素" + i);
            }).start());
        }
    
    }
    
    class BoundedContainer {
        private String[] elements = new String[10];
        private Lock lock = new ReentrantLock();
        private Condition notFull = lock.newCondition();
        private Condition notEmpty = lock.newCondition();
    
        // element count
        private int count;
        private int putIndex, takeIndex;
    
        void put(String element) {
            this.lock.lock();
            try {
                while (this.count == this.elements.length) {
                    notFull.await();
                }
                elements[putIndex] = element;
    
                if (++putIndex == this.elements.length) {
                    putIndex = 0;
                }
                ++count;
    
                System.out.println("put method: " + Arrays.toString(elements));
                notEmpty.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.lock.unlock();
            }
        }
    
        String take() {
            this.lock.lock();
            try {
                while (this.count == 0) {
                    notEmpty.await();
                }
    
                String element = elements[takeIndex];
                elements[takeIndex] = null;
                if (++takeIndex == this.elements.length) {
                    takeIndex = 0;
                }
                --count;
    
                System.out.println("take method: " + Arrays.toString(elements));
                notFull.signal();
    
                return element;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.lock.unlock();
            }
            return null;
        }
    }
    
    

    程序执行结果

    "D:\Program Files (x86)\java\jdk8_152\bin\java" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.1\lib\idea_rt.jar=65493:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files (x86)\java\jdk8_152\jre\lib\charsets.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\deploy.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\access-bridge-64.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\cldrdata.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\dnsns.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\jaccess.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\jfxrt.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\localedata.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\nashorn.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\sunec.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\sunjce_provider.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\sunmscapi.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\sunpkcs11.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\ext\zipfs.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\javaws.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\jce.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\jfr.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\jfxswt.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\jsse.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\management-agent.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\plugin.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\resources.jar;D:\Program Files (x86)\java\jdk8_152\jre\lib\rt.jar;E:\code\interview\thread\out\production\thread" com.learn.thread.condition.ConditionTest2
    put method: [元素1, null, null, null, null, null, null, null, null, null]
    put method: [元素1, 元素0, null, null, null, null, null, null, null, null]
    put method: [元素1, 元素0, 元素2, null, null, null, null, null, null, null]
    take method: [null, 元素0, 元素2, null, null, null, null, null, null, null]
    元素1
    put method: [null, 元素0, 元素2, 元素3, null, null, null, null, null, null]
    put method: [null, 元素0, 元素2, 元素3, 元素5, null, null, null, null, null]
    take method: [null, null, 元素2, 元素3, 元素5, null, null, null, null, null]
    元素0
    take method: [null, null, null, 元素3, 元素5, null, null, null, null, null]
    元素2
    put method: [null, null, null, 元素3, 元素5, 元素4, null, null, null, null]
    take method: [null, null, null, null, 元素5, 元素4, null, null, null, null]
    元素3
    take method: [null, null, null, null, null, 元素4, null, null, null, null]
    元素5
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素4
    put method: [null, null, null, null, null, null, 元素8, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素8
    put method: [null, null, null, null, null, null, null, 元素9, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素9
    put method: [null, null, null, null, null, null, null, null, 元素10, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素10
    put method: [null, null, null, null, null, null, null, null, null, 元素12]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素12
    put method: [元素13, null, null, null, null, null, null, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素13
    put method: [null, 元素14, null, null, null, null, null, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素14
    put method: [null, null, 元素6, null, null, null, null, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素6
    put method: [null, null, null, 元素7, null, null, null, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素7
    put method: [null, null, null, null, 元素11, null, null, null, null, null]
    take method: [null, null, null, null, null, null, null, null, null, null]
    元素11
    
    Process finished with exit code 0
    
    
    展开全文
  • 主要介绍了Java使用Condition控制线程通信的方法,结合实例形式分析了使用Condition类同步检测控制线程通信的相关操作技巧,需要的朋友可以参考下
  • 主要介绍了MySQL Index Condition Pushdown(ICP)性能优化方法实例,本文讲解了概念介绍、原理、实践案例、案例分析、ICP的使用限制等内容,需要的朋友可以参考下
  • 主要介绍了Lock、Condition实现简单的生产者消费者模式示例,需要的朋友可以参考下
  • 深入理解Condition实现原理

    千次阅读 2020-06-08 23:15:03
    本篇文章主要是讲condition实现原理(即等待队里),同步队列实现原理看这篇文章:深入理解AQS实现原理和源码分析。等待队列是由Condition内部实现的,是一个虚拟的FIFO单向队列,在AQS中同步队列、...

    在AQS中存在两个FIFO队列:同步队列 和 等待队列。本篇文章主要是讲condition实现原理(即等待队里),同步队列实现原理看这篇文章:深入理解AQS实现原理和源码分析。等待队列是由Condition内部实现的,是一个虚拟的FIFO单向队列,在AQS中同步队列、等待队列组成关系如下图:

    • (1)AQS中tail 和 head主要构成了一个FIFO双向的同步队列。

    • (2)AQS中condition构成了一个FIFO单向等待队列。condition是AQS内部类,每个Condition对象中保存了firstWaiter和lastWaiter作为队列首节点和尾节点,每个节点使用Node.nextWaiter保存下一个节点的引用,因此等待队列是一个单向队列。

    1.队列关系

    在Object的监视器(monitor)模型上,一个对象拥有一个同步队列和一个等待队列;而并发包中的AQS上拥有一个同步队列和多个等待队列。两者的具体实现原理的有所不同,但在多线程下等待/唤醒 操作的思路有相同之处,Object的监视器模型 和 AQS对同步队列、等待队列对应关系如下图

    (1)Object的监视器模型同步、等待队列对应关系图

    多个线程并发访问某个对象监视器(Monitor对象)的时候,即多线程执行Synchonized处的代码时,monitor处理过程包括:

    • (1)thread进入Synchonized代码时,会执行Monitor.Enter命令来获取monitor对象。如果命令执行成功获取Monitor对象成功,执行失败线程会进入synchronized同步队列中,线程处于BLOCKED,直到monitor对象被释放。

    • (2)thread执行完Synchonized同步代码块后,会执行Monitor.exit命令来释放monitor对象,并通知同步队列会获取monitor对象。

    • (3)如果线程执行object.wait(),线程会进入synchronized等待队列进行WAITING,直到其他线程线程执行notify()或notifyAll()方法,将等待队列中的一个或多个等待线程从等待队列中移到同步队列中,被移动的线程状态由WAITING变为BLOCKED。

    (2)AQS中同步、等待队列对应关系图

    当多线程并发访问AQS的lock()、await()、single()方法时,同步队列和等待队列变化处理过程包括:

    • (1)多个形成执行lock()方法时,线程会竞争获取同步锁state,获取成功的线程占有锁state、获取失败的线程会封装成node加入到AQS的同步队列中,等待锁state的释放。
    • (2)等获取了state锁的线程(同步队列中head节点)执行await()方法时,condition会将当前线程封装成一个新的node添加到condition等待队列的尾部,同时阻塞(waiting),直到被唤醒。
    • (3)等获取了state锁的线程(同步队列中head节点)single()方法时,condition会将等待队列首节点移动到同步队列的尾部,直到获取同步锁state才被唤醒。

    两者的对比关系图:

    2.Condition的实现

    (1)等待的实现

    当线程调用Condition.await()方法时,将会把前线程封装成node节点,并将节点加入等待队列的尾部,然后释放同步state状态,唤醒同步队列中的后继节点,然后当前线程会进入等待状态。当前线程加入Condition的等待队列逻辑如下图:

    • (1)能够调用Condition.await()方法的节点是获取了同步state锁的node,即同步队列中的head节点;调用Condition的await()方法(或者以await开头的方法)会使当前线程进入等待队列并释放锁、唤醒同步队列中的后继节点,最后线程状态变为等待状态。

    • (2)Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter指向它,并且更新尾节点即可。

    • (3)调用Condition.await()节点引用更新的过程并没有使用CAS保证,原因在于调用await()方法的线程必定是获取了state锁的线程,也就是说该过程是由锁来保证线程安全的。

    注意:同步队列的首节点并不会直接加入等待队列,而是把当前线程构封装成一个新的节点并将其加入等待队列中。

    await()方法源码

    整个await()的执行的过程可以总结如下几步:

    • (1)将当前线程封装成node加入Condition等待队列尾部。

    • (2)释放state锁:不管重入几次,都把state释放为0,同时唤醒同步队列的后继节点。

    • (3)自旋:直到node节点在等待队列上的节点移动到了同步队列(通过其他线程调用signal())或被中断。

    • (4)阻塞当前节点,直到node获取到了锁,也就是node在同步队列上的节点排队排到了队首。

    
     
    1. public final void await() throws InterruptedException {
    2. //如果当前线程中断,抛出异常
    3. if (Thread.interrupted())
    4. throw new InterruptedException();
    5. //1.将当前线程封装成一个node并加入到等待队列队尾(这里如果lastWaiter是CANCELLED取消状态,那么会把它踢出Condition队列)。
    6. Node node = addConditionWaiter();
    7. //2.释放当前线程的独占锁,不管重入几次,都把state释放为0
    8. int savedState = fullyRelease(node);
    9. int interruptMode = 0;
    10. //3.判断当前节点没有在同步队列上,没有在同步队列上(即还没有被signal),则将当前线程阻塞
    11. while (!isOnSyncQueue(node)) {
    12. LockSupport.park( this);
    13. //4.判断标记两种中断:是在被signal前中断还是在被signal后中断,分别标记上THROW_IE和REINTERRUPT。
    14. if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
    15. break;
    16. }
    17. //5.这个时候线程已经被signal()或者signalAll()操作给唤醒了,退出了3中的while循环自旋等待,尝试再次获取锁
    18. if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    19. interruptMode = REINTERRUPT;
    20. if (node.nextWaiter != null) // clean up if cancelled 清除等待队列中等待状态不为CONDITION的节点
    21. unlinkCancelledWaiters();
    22. //6.在第4步中标记的中断状态,如果是在被signal前中断还是在被signal后中断,如果是被signal前就被中断则抛出 InterruptedException,否则执行 Thread.currentThread().interrupt();
    23. if (interruptMode != 0)
    24. reportInterruptAfterWait(interruptMode);
    25. }

    addConditionWaiter源码

    
     
    1. //addConditionWaiter()方法主要是将线程封装成节点,添加到等待队列尾部
    2. private Node addConditionWaiter() {
    3. Node t = lastWaiter;
    4. //Condition里面的节点状态不是等待状态CONDITION时,会清除节点
    5. if (t != null && t.waitStatus != Node.CONDITION) {
    6. unlinkCancelledWaiters();
    7. t = lastWaiter;
    8. }
    9. //将当前线程分装成一个node,加到等待多了尾部
    10. Node node = new Node(Thread.currentThread(), Node.CONDITION);
    11. if (t == null)
    12. firstWaiter = node;
    13. else
    14. t.nextWaiter = node;
    15. lastWaiter = node;
    16. return node;
    17. }
    18. //从等待队列头部开始循环清除等待状态waitStatus不为CONDITION的节点
    19. private void unlinkCancelledWaiters() {
    20. Node t = firstWaiter;
    21. Node trail = null;
    22. while (t != null) {
    23. Node next = t.nextWaiter;
    24. if (t.waitStatus != Node.CONDITION) {
    25. t.nextWaiter = null;
    26. if (trail == null)
    27. firstWaiter = next;
    28. else
    29. trail.nextWaiter = next;
    30. if (next == null)
    31. lastWaiter = trail;
    32. }
    33. else
    34. trail = t;
    35. t = next;
    36. }
    37. }

    fullyRelease()源码

    fullyRelease()方法中会调用release()释放掉state锁,不管重入几次,都把state释放为0,同时唤醒同步队列的后继节点。

    
     
    1. final int fullyRelease(Node node) {
    2. boolean failed = true;
    3. try {
    4. //获取持有state锁的次数
    5. int savedState = getState();
    6. //把state释放为0
    7. if (release(savedState)) {
    8. failed = false;
    9. return savedState;
    10. } else {
    11. throw new IllegalMonitorStateException();
    12. }
    13. } finally {
    14. //释放锁失败,再将node设置为CANCELLED状态
    15. if (failed)
    16. node.waitStatus = Node.CANCELLED;
    17. }
    18. }

    isOnSyncQueue()源码

    isOnSyncQueue()方法主要是判断node是不是在同步队列中,只有在同步队列中的线程才会被阻塞。

    
     
    1. final boolean isOnSyncQueue(Node node) {
    2. //如果当前节点状态是CONDITION或node.prev是null,则证明当前节点在等待队列上而不是同步队列上。用node.prev来判断,是因为一个节点如果要加入同步队列,在加入前就会设置好prev字段。
    3. if (node.waitStatus == Node.CONDITION || node.prev == null)
    4. return false;
    5. //如果node.next不为null,则一定在同步队列上,因为node.next是在节点加入同步队列后设置的
    6. if (node.next != null) // If has successor, it must be on queue
    7. return true;
    8. //从等待队列的尾部遍历判断node是否在等待队列中
    9. return findNodeFromTail(node);
    10. }
    11. private boolean findNodeFromTail(Node node) {
    12. Node t = tail;
    13. for (;;) {
    14. if (t == node)
    15. return true;
    16. if (t == null)
    17. return false;
    18. t = t.prev;
    19. }
    20. }

    reportInterruptAfterWait()方法源码

    reportInterruptAfterWait()方法会根据中断状态来判断是抛出异常,还是执行中断。即判断线程是在被signal前中断,还是在被signal后中断;如果是被signal前就被中断则抛出 InterruptedException,否则执行 Thread.currentThread().interrupt()。

    
     
    1. private void reportInterruptAfterWait(int interruptMode)
    2. throws InterruptedException {
    3. //如果是在调signal()前就被中断则抛出异常
    4. if (interruptMode == THROW_IE)
    5. throw new InterruptedException();
    6. //如果是在调signal()方法后中断,就执行中断
    7. else if (interruptMode == REINTERRUPT)
    8. selfInterrupt();
    9. }

    到此condition的wait()方法分析就完了,可以看出,await()的操作过程和Object.wait()方法是一样,只不过await()采用了Condition等待队列的方式实现了Object.wait()的功能。

    (2)通知的实现

    调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将等待队列中节点移到同步队列中。Condition的signal()方法将节点从等待队列移动到同步队列逻辑如下图:

    整个signal()的过程可以总结如下:

    • (1)执行signal()唤醒线程时,先判断当前线程是否是同步锁state持有线程,所以能够调用signal()方法的线程一定持有了同步锁state。

    • (2)自旋唤醒等待队列的firstWaiter(首节点),在唤醒firstWaiter节点之前,会将等待队列首节点移到同步队列中。

    signal()源码

    
     
    1. public final void signal() {
    2. //判断当前线程是否是state锁持有线程
    3. if (!isHeldExclusively())
    4. throw new IllegalMonitorStateException();
    5. Node first = firstWaiter;
    6. //等待队列首节点不为null时,唤醒首节点
    7. if (first != null)
    8. doSignal(first);
    9. }
    10. //自旋唤醒首节点
    11. private void doSignal(Node first) {
    12. do {
    13. //移动头节点指针firstWaiter
    14. if ( (firstWaiter = first.nextWaiter) == null)
    15. lastWaiter = null;
    16. //从等待队列中移除首节点
    17. first.nextWaiter = null;
    18. } while (!transferForSignal(first) //transferForSignal方法尝试唤醒当前节点,如果唤醒失败,则继续尝试唤醒
    19. && (first = firstWaiter) != null);
    20. }
    21. //尝试唤醒当前节点,并将当前节点移动到同步队列中
    22. final boolean transferForSignal(Node node) {
    23. //如果当前节点状态为CONDITION,则将状态改为0准备加入同步队列;如果当前状态不为CONDITION,说明该节点等待已被中断
    24. if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
    25. return false;
    26. //将node添加到同步队列中,返回的p是node节点在同步队列中的先驱节点
    27. Node p = enq(node);
    28. int ws = p.waitStatus;
    29. //如果先驱节点的状态为CANCELLED(大于0) 或设置先驱节点的状态为SIGNAL失败,那么就立即唤醒当前节点对应的线程,线程被唤醒后会执行await()方法中的acquireQueued()方法,该方法会重新尝试将节点的先驱状态设为SIGNAL并再次park线程;如果当前设置前驱节点状态为SIGNAL成功,那么就不需要马上唤醒线程了,当它的前驱节点成为同步队列的首节点且释放同步状态后,会自动唤醒它。
    30. if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    31. LockSupport.unpark(node.thread);
    32. return true;
    33. }

    signalAll()源码

    signalAll()会从首节点循环遍历等待队列,将等待队列中的所有节点移到同步队列中去。

    
     
    1. public final void signalAll() {
    2. //判断当前线程是否是state锁持有线程
    3. if (!isHeldExclusively())
    4. throw new IllegalMonitorStateException();
    5. Node first = firstWaiter;
    6. if (first != null)
    7. doSignalAll(first);
    8. }
    9. //遍历等待队列,将等待队列中的node移动到同步队里中
    10. private void doSignalAll(Node first) {
    11. lastWaiter = firstWaiter = null;
    12. do {
    13. Node next = first.nextWaiter;
    14. first.nextWaiter = null;
    15. //移动节点到同步队里中
    16. transferForSignal(first);
    17. first = next;
    18. } while (first != null);
    19. }

    (3)总结

    • (1)Condition等待通知的本质就是等待队列 和 同步队列的交互的过程,跟object的wait()/notify()机制一样;Condition是基于同步锁state实现的,而objec是基于monitor模式实现的。

    • (2)一个lock(AQS)可以有多个Condition,即多个等待队列,只有一个同步队列。

    • (3)Condition.await()方法执行时,会将同步队列里的head锁释放掉,把线程封装成新node添加到等待队列中;Condition.signal()方法执行时,会把等待队列中的首节点移到同步队列中去,直到锁state被获取才被唤醒。

    展开全文
  • 前端项目-condition.zip

    2019-09-03 13:06:01
    前端项目-condition,Advanced condition library
  • neumann-boundary-condition:自然共形映射
  • 主要介绍了Python中使用Queue模块和Condition对象进行线程同步的方法,配合threading模块下的线程编程进行操作的实例,需要的朋友可以参考下
  • An approach to generating test data for EFSM paths considering condition coverage
  • java中Condition类的详细介绍(详解)

    万次阅读 多人点赞 2019-08-04 17:09:18
    condition 介绍及demo  Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程...

    已找不到原文了,还望原文博主看到能告诉小白一下,一定标注原文地址

    一 condition 介绍及demo
     Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

    Condition是个接口,基本的方法就是await()和signal()方法;
    Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
    调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
      Conditon中的await()对应Object的wait();

    Condition中的signal()对应Object的notify();

    Condition中的signalAll()对应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;
    /**
     * 
     * @author zhangliang
     *
     * 2016年4月8日 下午5:48:54
     */
    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("我发出了一个信号:"+this.currentThread().getName());
    	                } finally{
    	                    lock.unlock();
    	                }
    	            }
    	 }
    	    
    }
    

    运行结果:
    在这里插入图片描述
    Condition的执行方式,是当在线程Consumer中调用await方法后,线程Consumer将释放锁,并且将自己沉睡,等待唤醒,线程Producer获取到锁后,开始做事,完毕后,调用Condition的signalall方法,唤醒线程Consumer,线程Consumer恢复执行。

    以上说明Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。

    Condition实现生产者、消费者模式:

    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("队列空,等待数据");
    	                            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();
    	                }
    	            }
    	        }
    	    }
    	}
    

    运行结果:

    在这里插入图片描述
    二 Condition接口
    condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。

    await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
    await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态
    awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
    awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
    awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
    signal() :唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
    signal()All :唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。

    三 condition实现分析:

    在这里插入图片描述

    Condition接口包含了多种await方式和两个通知方法
    ConditionObject实现了Condition接口,是AbstractQueuedSynchronizer的内部类(因为Condition的操作都需要获取想关联的锁)
    Reentrantlock的newCondition方法返回与某个lock实例相关的Condition对象

    public abstract class AbstractQueuedLongSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {
    

    结合上面的类图,我们看到condition实现是依赖于aqs,而aqs是个抽象类。里面定义了同步器的基本框架,实现了基本的结构功能。只留有状态条件的维护由具体同步器根据具体场景来定制,如常见的 ReentrantLock 、 RetrantReadWriteLock和CountDownLatch 等等,

    3.1 等待队列
    Condition是AQS的内部类。每个Condition对象都包含一个队列(等待队列)。等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。AQS有一个同步队列和多个等待队列,节点都是Node。等待队列的基本结构如下所示。

    在这里插入图片描述

    等待分为首节点和尾节点。当一个线程调用Condition.await()方法,将会以当前线程构造节点,并将节点从尾部加入等待队列。新增节点就是将尾部节点指向新增的节点。节点引用更新本来就是在获取锁以后的操作,所以不需要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 等待
    当线程调用了Condition的await()方法以后。线程就作为队列中的一个节点被加入到等待队列中去了。同时会释放锁的拥有。当从await方法返回的时候。当前线程一定会获取condition相关联的锁。

    如果从队列(同步队列和等待队列)的角度去看await()方法,当调用await()方法时,相当于同步队列的首节点(获取锁的节点)移动到Condition的等待队列中。

    调用该方法的线程成功的获取锁的线程,也就是同步队列的首节点,该方法会将当前线程构造成节点并加入到等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,然后当前线程会进入等待状态。

    当等待队列中的节点被唤醒的时候,则唤醒节点的线程开始尝试获取同步状态。如果不是通过 其他线程调用Condition.signal()方法唤醒,而是对等待线程进行中断,则会抛出InterruptedException异常信息。

    在这里插入图片描述

    我们看一下这个await的方法,它是AQS的方法,

    public final void await() throws InterruptedException {
    	if (Thread.interrupted())
    throw new InterruptedException();
    Node node = addConditionWaiter(); //将当前线程包装下后,
    //添加到Condition自己维护的一个链表中。
    int savedState = fullyRelease(node);//释放当前线程占有的锁,从demo中看到,
    //调用await前,当前线程是占有锁的
    
    int interruptMode = 0;
    	 while (!isOnSyncQueue(node)) {//释放完毕后,遍历AQS的队列,看当前节点是否在队列中,
    //不在 说明它还没有竞争锁的资格,所以继续将自己沉睡。
    //直到它被加入到队列中,聪明的你可能猜到了,
    //没有错,在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);
    }
    

    结合代码去看,同步队列的首节点 并不会直接加入等待队列,而是通过addConditionWaiter把当前线程构造成一个新节点并加入到等待队列中。

    /**
             * 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 通知
    调用Condition的signal()方法,将会唤醒在等待队列中等待最长时间的节点(条件队列里的首节点),在唤醒节点前,会将节点移到同步队列中。当前线程加入到等待队列中如图所示:

    在这里插入图片描述

    回到上面的demo,锁被释放后,线程Consumer开始沉睡,这个时候线程因为线程Consumer沉睡时,会唤醒AQS队列中的头结点,所所以线程Producer会开始竞争锁,并获取到,执行完后线程Producer会调用signal方法,“发出”signal信号,signal方法如下:

    public final void signal() {
    	 if (!isHeldExclusively())
    	 throw new IllegalMonitorStateException();
    	 Node first = firstWaiter; //firstWaiter为condition自己维护的一个链表的头结点,
    	                          //取出第一个节点后开始唤醒操作
    	 if (first != null)
    	 doSignal(first);
    	 }
    

    在调用signal()方法之前必须先判断是否获取到了锁(isHeldExclusively方法)。接着获取等待队列的首节点,将其移动到同步队列并且利用LockSupport唤醒节点中的线程。
    被唤醒的线程将从await方法中的while循环中退出( while (!isOnSyncQueue(node)) { 方法返回true,节点已经在同步队列中)。随后调用同步器的acquireQueued()方法加入到同步状态的竞争当中去。成功获取到竞争的线程从先前调用await方法返回,此时该线程已经成功获取了锁。


    AQS的同步队列与Condition的等待队列,两个队列的作用是不同,事实上,每个线程也仅仅会同时存在以上两个队列中的一个,流程是这样的:

    在这里插入图片描述

    注意:

    1.线程producer调用signal方法,这个时候Condition的等待队列中只有线程Consumer一个节点,于是它被取出来,并被加入到AQS的等待队列中。 注意,这个时候,线程Consumer 并没有被唤醒。

    2.Sync是AQS的抽象子类,实现可重入和互斥的大部分功能。在Sync的子类中有FairSync和NonfairSync两种代表公平锁策略和非公平锁策略。Sync lock方法留给子类去实现,NonfairSync的实现:

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

    其中如果一开始获取锁成功,是直接设置当前线程。
    否则执行acquire(1),也就是进入aqs等待队列。这里不展开细节。

    可以这样理解,整个协作过程是靠结点在AQS的等待队列和Condition的等待队列中来回移动实现的,每个队列的意义不同,Condition作为一个条件类,很好的自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 380,291
精华内容 152,116
关键字:

condition