jdk1.5提供的api,位于java.util.concurrent.locks 包下,是一个接口, 常用方法有:
方法 | 描述 |
---|---|
void await() | 造成当前线程在接到信号或被中断之前一直处于等待状态。 |
boolean await(long time, TimeUnit unit) | 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 |
long awaitNanos(long nanosTimeout) | 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 |
void awaitUninterruptibly() | 造成当前线程在接到信号之前一直处于等待状态。 |
boolean awaitUntil(Date deadline) | 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。 |
void signal() | 唤醒一个等待线程。 |
void signalAll() | 唤醒所有等待线程。 |
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
Condition (条件,也称为条件队列或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
官方示例
实现一个缓冲队列,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
class BoundedBuffer {
final Lock lock = new ReentrantLock();//定义锁对象
//通过锁对象获取Condition实例
final Condition notFull = lock.newCondition(); //用于控制put操作
final Condition notEmpty = lock.newCondition(); //用于控制take操作
final Object[] items = new Object[100];//缓冲队列,初始容量100
int putptr, takeptr, count;//分别记录put,take当前的索引,count用于记录当前item的个数
/**
* 往缓冲队列中添加数据
*/
public void put(Object x) throws InterruptedException {
//上锁,作用和synchronized一样,保证代码同一时刻只有一个线程可以操作,也保证了和take方法的互斥
lock.lock();
try {
while (count == items.length){
notFull.await();//如果队列满了,则put线程等待被唤醒
}
items[putptr] = x; //队列未满,则添加数据
if (++putptr == items.length) putptr = 0;//添完后,如果记录读数据的索引到了最后一个位置,则重置为0
++count;//item总数自增
notEmpty.signal();//唤醒take线程取数据
} finally {
lock.unlock();//put操作完后,释放锁.
}
}
/**
* 从缓冲队列中取数据
*/
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0){
notEmpty.await();//如果队列空了,则take线程等待被唤醒
}
Object x = items[takeptr]; //队列未空,则取数据
if (++takeptr == items.length) takeptr = 0;//取完后,如果记录取数据的索引到了最后一个位置,则重置为0
--count;//item总数自减
notFull.signal();//唤醒put线程添加数据
return x;//返回取得的数据
} finally {
lock.unlock();//take操作完后,释放锁对象
}
}
}
示例2
这个例子是用来实现如下功能,主线程运行5次,然后通知线程1运行5次,然后线程1通知线程2运行5次,接着又由线程2通知主线程运行5次,如此反复执行3次.
分析这个需求的时候,首先应该想到这里一共有3个线程,且每个线程在运行的时候,其它线程不能打扰,也就是说这3个线程需要共用同一个锁对象,即这3个线程需要访问同一个共享资源,这里可以有一个很好的解决思路就是将这3个线程各自运行5次的逻辑(方法)封装到同一个类中.这样,我们使用Lock或者synchronized的时候,只要锁对象是同一个,就能保证这3个方法的互斥性了.
然后,需要保证的是这3个线程在执行各自方法的时候是按照一定的顺序执行的,这里使用Condition就能很好的解决这个问题,需要用到3个Condition实例,第一个用于主线程与线程1通信,第二个用于线程1和线程2通信,第三个用于线程2和主线程通信.
package com.example;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by mChenys on 2016/5/15.
*/
public class ThreeConditionCommunication {
/**
* @param args
*/
public static void main(String[] args) {
//定义共享资源
final Business business = new Business();
//线程2执行
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
business.doSub2(i);
}
}
}, "Thread-1").start();
//线程3执行
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
business.doSub3(i);
}
}
}, "Thread-2").start();
//主线程执行
for (int i = 1; i <= 3; i++) {
business.doMain(i);
}
}
/**
* 共享资源
*/
static class Business {
//定义公共锁
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();//控制主线程运行
Condition condition2 = lock.newCondition();//控制线程2执行
Condition condition3 = lock.newCondition();//控制线程3执行
private int shouldSub = 1;//标记当前那个线程运行的标记,1为主线程,2为线程2,3为线程3
public void doMain(int i) {
lock.lock();//上锁
try {
while (shouldSub != 1) {
try {
condition1.await();//主线程等待
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 5; j++) {
System.out.println("doMain thread sequence of " + j + ",loop of " + i);
}
shouldSub = 2;
condition2.signal();//唤醒线程2
} finally {
lock.unlock();//释放锁
}
}
public void doSub2(int i) {
lock.lock();//上锁
try {
while (shouldSub != 2) {
try {
condition2.await();//线程2等待
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 5; j++) {
System.out.println("doSub2 thread sequence of " + j + ",loop of " + i);
}
shouldSub = 3;
condition3.signal();//唤醒线程3
} finally {
lock.unlock();//释放锁
}
}
public void doSub3(int i) {
lock.lock();//上锁
try {
while (shouldSub != 3) {
try {
condition3.await();//线程3等待
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 5; j++) {
System.out.println("doSub3 thread sequence of " + j + ",loop of " + i);
}
shouldSub = 1;
condition1.signal();//唤醒主线程
} finally {
lock.unlock();//释放锁
}
}
}
}
运行结果:
doMain thread sequence of 1,loop of 1
doMain thread sequence of 2,loop of 1
doMain thread sequence of 3,loop of 1
doMain thread sequence of 4,loop of 1
doMain thread sequence of 5,loop of 1
doSub2 thread sequence of 1,loop of 1
doSub2 thread sequence of 2,loop of 1
doSub2 thread sequence of 3,loop of 1
doSub2 thread sequence of 4,loop of 1
doSub2 thread sequence of 5,loop of 1
doSub3 thread sequence of 1,loop of 1
doSub3 thread sequence of 2,loop of 1
doSub3 thread sequence of 3,loop of 1
doSub3 thread sequence of 4,loop of 1
doSub3 thread sequence of 5,loop of 1
doMain thread sequence of 1,loop of 2
doMain thread sequence of 2,loop of 2
doMain thread sequence of 3,loop of 2
doMain thread sequence of 4,loop of 2
doMain thread sequence of 5,loop of 2
doSub2 thread sequence of 1,loop of 2
doSub2 thread sequence of 2,loop of 2
doSub2 thread sequence of 3,loop of 2
doSub2 thread sequence of 4,loop of 2
doSub2 thread sequence of 5,loop of 2
doSub3 thread sequence of 1,loop of 2
doSub3 thread sequence of 2,loop of 2
doSub3 thread sequence of 3,loop of 2
doSub3 thread sequence of 4,loop of 2
doSub3 thread sequence of 5,loop of 2
doMain thread sequence of 1,loop of 3
doMain thread sequence of 2,loop of 3
doMain thread sequence of 3,loop of 3
doMain thread sequence of 4,loop of 3
doMain thread sequence of 5,loop of 3
doSub2 thread sequence of 1,loop of 3
doSub2 thread sequence of 2,loop of 3
doSub2 thread sequence of 3,loop of 3
doSub2 thread sequence of 4,loop of 3
doSub2 thread sequence of 5,loop of 3
doSub3 thread sequence of 1,loop of 3
doSub3 thread sequence of 2,loop of 3
doSub3 thread sequence of 3,loop of 3
doSub3 thread sequence of 4,loop of 3
doSub3 thread sequence of 5,loop of 3
从运行结果,可以看出每个线程在执行的时候都是完整完成的,不会受到其它线程的打扰,同时各线程间的运行都是按照事先定好的规则来实现的,这就体现了Condition的强大之处,它能完成wait、notify所不能完成的事情,wait、notify 只能控制2个线程间的通信,而Condition可以控制n个线程间的通信.