上一篇文章里讲了java.util.concurrent.locks 包下的Lock,以及简单使用的例子,这一篇我们要继续介绍java.util.concurrent.locks包下的类文件,它就是Condition
一:源码解读
package java.util.concurrent.locks; import java.util.concurrent.*; import java.util.Date; /** * Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象, * 以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set) * 其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用 */ public interface 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(); }
说明:通过查看Condition的源码,大致了解到两个要点
- 它是一个接口
- 它提供的一系列方法都是用来阻塞或唤醒线程用的。(功能类似Java中Object类的wait(),notify()和notifyAll()方法)
JDK的官方解释如下:
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。 Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
在JDK的源码中,还提供了一个关于Condition示例,示例如下:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public 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();//解锁 } } }
这里使用了ReentrantLock锁和Condition对象给出了有界缓存的实现,即使用Condition,分别为notFull和 notEmpty。当缓存为空时,take将阻塞并等待notEmpty,此时put向notEmpty发送信号,可以解除任何在take中阻塞的线程。
二:实际运用
我们要打印1到9这9个数字,由A线程先打印1,2,3,然后由B线程打印4,5,6,然后再由A线程打印7,8,9. 这道题有很多种解法,现在我们使用Condition来做这道题。
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class PrintDemo { //申请锁 final Lock lock = new ReentrantLock(); final Condition conditionA = lock.newCondition(); final Condition conditionB = lock.newCondition(); private String worker = "A"; //默认是A先工作 private int endNum = 9; private int i = 1; public static void main(String[] args){ PrintDemo pd = new PrintDemo(); pd.print(); } public void print(){ new Thread(new Runnable() { @Override public void run() { lock.lock();//加锁 try{ while(i<endNum){ while(!worker.equals("A")){ conditionA.await(); //如果worker不是A,表示A不需要工作,则处于休眠等待 } System.out.println(i + "--" + Thread.currentThread().getName()); if(i%3==0){ worker = "B"; conditionB.signal(); } i++; } System.out.println(i + "--" + Thread.currentThread().getName()); }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock();//解锁 } } }, "threadA").start(); new Thread(new Runnable() { @Override public void run() { lock.lock();//加锁 try{ while(i<endNum){ while(!worker.equals("B")){ conditionB.await(); //如果worker不是B,表示B不需要工作,则处于休眠等待 } System.out.println(i + "--" + Thread.currentThread().getName()); if(i%3==0){ worker = "A"; conditionA.signal(); } i++; } }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock();//解锁 } } }, "threadB").start(); } }
运行结果:
1--threadA
2--threadA
3--threadA
4--threadB
5--threadB
6--threadB
7--threadA
8--threadA
9--threadA
通过使用Condition,有效了使线程A和线程B进行协作工作,A打印完1、2、3后,B打印4、5、6,然后A再打印7、8、9.其实这里只是简单的使用例子,真正的使用环境并不是打印字母这么简单。
在这里,我想抛出一个问题:为什么一个Lock可以创建两个Condition,它是怎么实现的?
参考资料:
http://blog.csdn.net/ghsau/article/details/7481142
http://ifeve.com/understand-condition/
http://www.th7.cn/Program/java/201308/147915.shtml
http://outofmemory.cn/java/java.util.concurrent/lock-reentrantlock-condition