JUC-Condition
在应用Lock之前,咱们应用的最多的同步形式应该是synchronized关键字来实现同步形式了。配合Object的wait()、notify()系列办法能够实现期待/告诉模式。Condition接口也提供了相似Object的监视器办法,与Lock配合能够实现期待/告诉模式,然而这两者在应用形式以及性能个性上还是有差异的。Object和Condition接口的一些比照。摘自《Java并发编程的艺术》
一、Condition接口介绍和示例
首先咱们须要明确condition对象是依赖于lock对象的,意思就是说condition对象须要通过lock对象进行创立进去(调用Lock对象的newCondition()办法)。consition的应用形式十分的简略。然而须要留神在调用办法前获取锁。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * ConditionDemo */public class ConditionDemo { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public static void main(String[] args) { ConditionDemo demo = new ConditionDemo(); ExecutorService executorService = Executors.newFixedThreadPool(4); // AQS, condition.signal()的信号会给队列首位的线程, executorService.execute(demo::conditionWait); executorService.execute(demo::conditionWait); executorService.execute(demo::conditionSignal); executorService.execute(demo::conditionSignal); } private void conditionWait() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "拿到锁了"); System.out.println(Thread.currentThread().getName() + "期待信号"); condition.await(); System.out.println(Thread.currentThread().getName() + "拿到信号"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } private void conditionSignal() { lock.lock(); try { Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + "拿到锁了"); condition.signal(); System.out.println(Thread.currentThread().getName() + "发出信号"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}
pool-1-thread-1拿到锁了pool-1-thread-1期待信号pool-1-thread-2拿到锁了pool-1-thread-2期待信号pool-1-thread-3拿到锁了pool-1-thread-3发出信号pool-1-thread-4拿到锁了pool-1-thread-4发出信号pool-1-thread-1拿到信号pool-1-thread-2拿到信号
如示例所示,个别都会将Condition对象作为成员变量。当调用await()办法后,以后线程会开释锁并在此期待,而其余线程调用Condition对象的signal()办法,告诉以后线程后,以后线程才从await()办法返回,并且在返回前曾经获取了锁。
二、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相干的锁。
- signalAll() :唤醒所有期待线程。可能从期待办法返回的线程必须取得与Condition相干的锁。
三、Condition接口原理简略解析
Condition是AQS的外部类。每个Condition对象都蕴含一个队列(期待队列)。期待队列是一个FIFO的队列,在队列中的每个节点都蕴含了一个线程援用,该线程就是在Condition对象上期待的线程,如果一个线程调用了Condition.await()办法,那么该线程将会开释锁、结构成节点退出期待队列并进入期待状态。期待队列的根本构造如下所示。
期待分为首节点和尾节点。当一个线程调用Condition.await()办法,将会以以后线程结构节点,并将节点从尾部退出期待队列。新增节点就是将尾部节点指向新增的节点。节点援用更新原本就是在获取锁当前的操作,所以不须要CAS保障。同时也是线程平安的操作。
3.1、期待
当线程调用了await办法当前。线程就作为队列中的一个节点被退出到期待队列中去了。同时会开释锁的领有。当从await办法返回的时候。肯定会获取condition相关联的锁。当期待队列中的节点被唤醒的时候,则唤醒节点的线程开始尝试获取同步状态。如果不是通过 其余线程调用Condition.signal()办法唤醒,而是对期待线程进行中断,则会抛出InterruptedException异样信息。
3.2、告诉
调用Condition的signal()办法,将会唤醒在期待队列中期待最长工夫的节点(条件队列里的首节点),在唤醒节点前,会将节点移到同步队列中。以后线程退出到期待队列中如图所示:
在调用signal()办法之前必须先判断是否获取到了锁。接着获取期待队列的首节点,将其挪动到同步队列并且利用LockSupport唤醒节点中的线程。节点从期待队列挪动到同步队列如下图所示:
被唤醒的线程将从await办法中的while循环中退出。随后退出到同步状态的竞争当中去。胜利获取到竞争的线程则会返回到await办法之前的状态。
四、总结
调用await办法后,将以后线程退出Condition期待队列中。以后线程开释锁。否则别的线程就无奈拿到锁而产生死锁。自旋(while)挂起,一直检测节点是否在同步队列中了,如果是则尝试获取锁,否则挂起。当线程被signal办法唤醒,被唤醒的线程将从await()办法中的while循环中退出来,而后调用acquireQueued()办法竞争同步状态。
五、利用Condition实现生产者消费者模式
import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * 利用Condition实现生产者消费者模式 */public class BoundedQueue { //生产者容器 private LinkedList<Object> buffer; //容器最大值是多少 private int maxSize; private Lock lock; private Condition fullCondition; private Condition notFullCondition; BoundedQueue(int maxSize) { this.maxSize = maxSize; buffer = new LinkedList<>(); lock = new ReentrantLock(); fullCondition = lock.newCondition(); notFullCondition = lock.newCondition(); } /** * 生产者 * * @param obj * @throws InterruptedException */ public void put(Object obj) throws InterruptedException { //获取锁 lock.lock(); try { while (maxSize >= buffer.size()) { //满了,增加的线程进入期待状态 notFullCondition.await(); } buffer.add(obj); //告诉 fullCondition.signal(); } finally { lock.unlock(); } } /** * 消费者 * * @return * @throws InterruptedException */ public Object get() throws InterruptedException { Object obj; lock.lock(); try { while (buffer.size() == 0) { //队列中没有数据了 线程进入期待状态 fullCondition.await(); } obj = buffer.poll(); //告诉 notFullCondition.signal(); } finally { lock.unlock(); } return obj; }}
参考:
Java并发编程的艺术
Java并发编程网