Java5的条件阻塞条件
条件的功能类似在传统线程技术中的的Object.wait()和Object.natify()的功能,传统线程技术实现的互斥只能一个线程单独执行,不能说这个线程执行完了通知另一个线程来执行,条件就是解决这个问题的,实现线程间的通信。比如CPU让线程1做事,线程1说我先歇着并通知线程2,线程2就开始做事。
公共接口条件
条件将对象监视器方法(wait,notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,条件替代了Object监视器方法的使用。
Condition实例,请使用其newCondition()方法。
JDK文档中的一个示例,假定有一个绑定的缓冲区,它支持put和take方法。如果试图在空的缓冲区上执行取操作,则在某个项目变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行放置操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待set中保存放线程和take线程,这样就可以在缓冲区中的项目或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个条件实例来做到这一点。
// 阻塞队列 满了不能放,空了不能取
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();
}
}
}
条件的使用方法
锁定锁= newReerantrantLock();
条件条件= lock.newCondition();
this.wait()---> condition.await()
this.notify()---> condition.signal()
注意:判断条件时用,同时防止虚假唤醒,等待在那里,唤醒后再进行判断,确认符合要求后再执行任务。
实例:实现效果:子线程循环10次,接着主线程循环100次,又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @Description: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次
* 使用 Lock 与 Condition 技术实现
*
* @author: zxt
*
* @time: 2018年4月6日 下午7:12:39
*
*/
public class ThreadCommunication {
public static void main(String[] args) {
final Business business = new Business();
// 子线程
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start();
// 主线程
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
}
/**
*
* @Description: 同步的功能封装在资源内部(这样调用它的线程则不再需要考虑线程同步问题)
*
* @author: zxt
*
* @time: 2018年4月6日 下午7:36:02
*
*/
class Business {
// 构造一把锁
Lock lock = new ReentrantLock();
// 由锁创建一个条件阻塞Condition对象
Condition condition = lock.newCondition();
// 表示是否应该sub函数执行(为true表示sub执行)
private boolean bSubShould = true;
public void sub(int i) {
lock.lock();
try {
while(!bSubShould) {
try {
// 非sub函数执行时,则等待
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("this is sub thread sequence of " + j + ", loop of " + i);
}
// 下次执行是main函数
bSubShould = false;
// 同时需要唤醒等待的线程(由condition给一个信号)
condition.signal();
} finally {
// 释放锁对象
lock.unlock();
}
}
public void main(int i) {
lock.lock();
try {
while(bSubShould) {
try {
// 非住线程执行时,则等待
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("this is main thread sequence of " + j + ", loop of " + i);
}
// 下次执行的是sub函数
bSubShould = true;
// 同时需要唤醒等待的线程(由condition给一个信号)
condition.signal();
} finally {
// 释放锁对象
lock.unlock();
}
}
}
在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现。
Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition 实例,请使用其newCondition() 方法。
通过上述实例发现,Condition和传统的线程通信没什么区别,而Condition的强大之处在于它可以为多个线程间建立不同的Condition,这样可以控制多个线程之间的运行顺序。
例如修改上述实例:实现子线程1循环10次,子线程2循环5次,接着主线程循环10次,然后又回到子线程1循环10次,子线程2循环5次,再回到主线程又循环10次,如此循环50次。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @Description: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次
* 使用 Lock 与 Condition 技术实现
*
* @author: zxt
*
* @time: 2018年4月6日 下午7:12:39
*
*/
public class ThreeThreadCommunication {
public static void main(String[] args) {
final Business business = new Business();
// 子线程1
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start();
// 子线程2
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i);
}
}
}).start();
// 主线程
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
/**
*
* @Description: 同步的功能封装在资源内部(这样调用它的线程则不再需要考虑线程同步问题)
*
* @author: zxt
*
* @time: 2018年4月6日 下午7:36:02
*
*/
static class Business {
// 构造一把锁
Lock lock = new ReentrantLock();
// 由锁创建条件阻塞Condition对象 (子线程1 通知 子线程2, 子线程2 通知 主线程, 主线程 通知 子线程1)
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
// 表示是否应该哪个函数执行(1:子线程1,2:子线程2, 3:主线程)
private int subShould = 1;
public void sub(int i) {
lock.lock();
try {
while(subShould != 1) {
try {
// 非sub函数执行时,则等待
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
}
// 下次执行是sub2函数
subShould = 2;
// 同时需要唤醒等待的线程2(由condition给一个信号)
condition2.signal();
} finally {
// 释放锁对象
lock.unlock();
}
}
public void sub2(int i) {
lock.lock();
try {
while(subShould != 2) {
try {
// 非sub2函数执行时,则等待
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
}
// 下次执行是m函数
subShould = 3;
// 同时需要唤醒等待的主线程(由condition给一个信号)
condition3.signal();
} finally {
// 释放锁对象
lock.unlock();
}
}
public void main(int i) {
lock.lock();
try {
while(subShould != 3) {
try {
// 非主线程执行时,则等待
condition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
}
// 下次执行的是sub函数
subShould = 1;
// 同时需要唤醒等待的线程1(由condition给一个信号)
condition1.signal();
} finally {
// 释放锁对象
lock.unlock();
}
}
}
}
总结
对于传统线程通信synchronized方式与Lock结合Condition的方式,Lock 替代了synchronized 方法和语句的使用,Condition 替代了Object 监视器方法的使用。而对比来说Lock (本身为一个对象)更加面向对象,Condition 可以为多个线程间建立不同的条件,这样可以控制多个线程之间的运行顺序。