上一篇文章里讲了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