1、Condition 类 在前面咱们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.concurrent.locks.ReentrantLock 锁,JDK也为咱们提供了与此功能相应的类java.util.concurrent.locks.Condition。Condition与重入锁是经过lock.newCondition()方法产生一个与当前重入锁绑定的 Condtion实例,咱们通知该实例来控制线程的等待与通知。该接口的全部方法: 复制代码 public interface Condition { //使当前线程加入 await() 等待队列中,并释放当锁,当其余线程调用signal()会从新请求锁。与Object.wait()相似。 void await() throws InterruptedException; //调用该方法的前提是,当前线程已经成功得到与该条件对象绑定的重入锁,不然调用该方法时会抛出IllegalMonitorStateException。 //调用该方法后,结束等待的惟一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程当中若是当前线程被 中断,该方法仍然会继续等待,同时保留该线程的中断状态。 void awaitUninterruptibly(); // 调用该方法的前提是,当前线程已经成功得到与该条件对象绑定的重入锁,不然调用该方法时会抛出IllegalMonitorStateException。 //nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间; //若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知, 则返回0或负数。 long awaitNanos(long nanosTimeout) throws InterruptedException; //与await()基本一致,惟一不一样点在于,指定时间以内没有收到signal()或signalALL()信号或者线程中断时该方法会返回 false;其它状况返回true。 boolean await(long time, TimeUnit unit) throws InterruptedException; //适用条件与行为与awaitNanos(long nanosTimeout)彻底同样,惟一不一样点在于它不是等待指定时间,而是等待由参数指定的 某一时刻。 boolean awaitUntil(Date deadline) throws InterruptedException; //唤醒一个在 await()等待队列中的线程。与Object.notify()类似 void signal(); //唤醒 await()等待队列中全部的线程。与object.notifyAll()类似 void signalAll(); } 复制代码 2、使用 一、await() 等待 与 singnal()通知 复制代码 1 package com.jalja.org.base.Thread; 2 3 import java.util.concurrent.TimeUnit; 4 import java.util.concurrent.locks.Condition; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 /** 8 * Condition 配合Lock 实现线程的等待 与通知 9 */ 10 public class ConditionTest{ 11 public static ReentrantLock lock=new ReentrantLock(); 12 public static Condition condition =lock.newCondition(); 13 public static void main(String[] args) { 14 new Thread(){ 15 @Override 16 public void run() { 17 lock.lock();//请求锁 18 try{ 19 System.out.println(Thread.currentThread().getName()+"==》进入等待"); 20 condition.await();//设置当前线程进入等待 21 }catch (InterruptedException e) { 22 e.printStackTrace(); 23 }finally{ 24 lock.unlock();//释放锁 25 } 26 System.out.println(Thread.currentThread().getName()+"==》继续执行"); 27 } 28 }.start(); 29 new Thread(){ 30 @Override 31 public void run() { 32 lock.lock();//请求锁 33 try{ 34 System.out.println(Thread.currentThread().getName()+"=》进入"); 35 Thread.sleep(2000);//休息2秒 36 condition.signal();//随机唤醒等待队列中的一个线程 37 System.out.println(Thread.currentThread().getName()+"休息结束"); 38 }catch (InterruptedException e) { 39 e.printStackTrace(); 40 }finally{ 41 lock.unlock();//释放锁 42 } 43 } 44 }.start(); 45 } 46 } 复制代码 执行结果: Thread-0==》进入等待 Thread-1=》进入 Thread-1休息结束 Thread-0==》继续执行 流程:在调用await()方法前线程必须得到重入锁(第17行代码),调用await()方法后线程会释放当前占用的锁。同理在调用 signal()方法时当前线程也必须得到相应重入锁(代码32行),调用signal()方法后系统会从condition.await()等待队列中唤醒一 个线程。当线程被唤醒后,它就会尝试从新得到与之绑定的重入锁,一旦获取成功将继续执行。因此调用signal()方法后必定要释放当 前占用的锁(代码41行),这样被唤醒的线程才能有得到锁的机会,才能继续执行。 3、JDK中对Condition 的使用 咱们来看看java.util.concurrent.ArrayBlockingQueue; 基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个经常使用的阻塞 队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。 看看他的put方法: 复制代码 public void put(E e) throws InterruptedException { checkNotNull(e);//对传入元素的null判断 final ReentrantLock lock = this.lock; lock.lockInterruptibly();//对put()方法作同步 try { while (count == items.length)//若是队列已满 notFull.await();//让当前添加元素的线程进入等待状态 insert(e);// 若是有其余线程调用signal() 通知该线程 ,则进行添加行为 } finally { lock.unlock();//释放锁 } } private E extract() { final Object[] items = this.items; E x = this.<E>cast(items[takeIndex]); items[takeIndex] = null; takeIndex = inc(takeIndex); --count; notFull.signal();//唤醒一个在Condition等待队列中的线程 return x; }