java5条件阻塞Condition的应用

一、概述

    1、在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

    2、一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。

二、代码描述

    1、ConditionCommunication.java

将传统wait()和notify() 改写成Condition方式,需要Lock配合使用。

/** 
* @Title: TraditionalThreadCommunication.java 
* @Package com.lh.threadtest.t4 
* @Description: TODO
* @author Liu 
* @date 2018年1月15日 下午9:28:23 
* @version V1.0 
*/
package com.lh.threadtest.t10;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* @ClassName: TraditionalThreadCommunication 
* @Description: 传统线程同步通信技术
* 
* 子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程
* 又循环100次,如此循环50次,请写出程序。
* 
* 注意:
* 	Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。
* 两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。锁是上在代表要操作的资源的类的内部方法中,
* 而不是线程代码中!
* 
* 总结:
* 	需要用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类中,这种设计正好体现了
* 高类聚和程序的健壮性。
* 
* @author Liu
* @date 2018年1月15日 下午9:28:23 
*  
*/
public class ConditionCommunication {
	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++){
					try {
						business.sub(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		for(int i = 1; i <= 50; i++){
			try {
				business.main(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
//不同点:	
//condition.business
//business
	
	//将具有类似功能的业务代码抽象到同一个类中
	//下面两个方法(sub/main)是互斥的,均是独立的一个同步模块,处于同一个互斥组(通过synchronized表明均属于同一个类,同一时刻只能有一个线程持有该锁)
	static class Business{
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		
		//主线程和子线程切换执行的一个标识
		boolean flag = true;
		
		void sub(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循环和if功能相仿,但更加完备
				while(!flag){
					//使当前线程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 10; j++){
					System.out.println("sub thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = false;
				
				//唤醒其它正在等待的线程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void main(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(flag){
				//循环和if功能相仿,但更加完备
				while(flag){
					//使当前线程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 100; j++){
					System.out.println("main thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = true;
				
				//唤醒其它正在等待的线程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

    2、ThreeConditionCommunication.java

老大循环100次,然后老二循环10次,接着老三循环20次,又接着老大循环100次,老二又循环10次,老三又循环20次, 如此循环50次,请写出程序。

/** 
* @Title: TraditionalThreadCommunication.java 
* @Package com.lh.threadtest.t4 
* @Description: TODO
* @author Liu 
* @date 2018年1月15日 下午9:28:23 
* @version V1.0 
*/
package com.lh.threadtest.t10;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* @ClassName: TraditionalThreadCommunication 
* @Description: java5条件阻塞Condition的应用
* 
* 老大循环100次,然后老二循环10次,接着老三循环20次,
* 又接着老大循环100次,老二又循环10次,老三又循环20次,
* 如此循环50次,请写出程序。
* 
* @author Liu
* @date 2018年1月15日 下午9:28:23 
*  
*/
public class ThreeConditionCommunication {
	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++){
					try {
						business.sub2(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			public void run() {
				for(int i = 1; i <= 50; i++){
					try {
						business.sub3(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		for(int i = 1; i <= 50; i++){
			try {
				business.main(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
//不同点:	
//condition.business
//business
	
	//将具有类似功能的业务代码抽象到同一个类中
	//下面两个方法(sub/main)是互斥的,均是独立的一个同步模块,处于同一个互斥组(通过synchronized表明均属于同一个类,同一时刻只能有一个线程持有该锁)
	static class Business{
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		
		//主线程和子线程切换执行的一个标识
		int flag = 1;
		
		void sub3(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循环和if功能相仿,但更加完备
				while(flag != 3){
					//使当前线程等待...
//					this.wait();
					condition3.await();
				}
				
				for(int j = 1; j <= 20; j++){
					System.out.println("sub3 thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 1;
				
				//唤醒其它正在等待的线程
//				this.notify();
				condition.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void sub2(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(!flag){
				//循环和if功能相仿,但更加完备
				while(flag != 2){
					//使当前线程等待...
//					this.wait();
					condition2.await();
				}
				
				for(int j = 1; j <= 10; j++){
					System.out.println("sub2 thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 3;
				
				//唤醒其它正在等待的线程
//				this.notify();
				condition3.signal();
			} finally {
				lock.unlock();
			}
		}
		
		void main(int i) throws InterruptedException{
			lock.lock();
			try {
//				if(flag){
				//循环和if功能相仿,但更加完备
				while(flag != 1){
					//使当前线程等待...
//					this.wait();
					condition.await();
				}
				
				for(int j = 1; j <= 100; j++){
					System.out.println("main thread sequence of "  + j + ", loop of " + i);
				}
				
				flag = 2;
				
				//唤醒其它正在等待的线程
//				this.notify();
				condition2.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

三、jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例

155055_laHz_3144678.png

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();
        }
    }
}

思考:

    1、为什么要用两个Condition,而不是一个?

      唤醒的是取的,而不是放的,每次只能存取一个。如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。

四、关键点

    1、Condition一般需要依赖Lock使用,不能独立发挥作用。

    2、Condition作用相当于传统线程中的wait()和notify()——线程间通信。

    3、wait()和notify()只能在synchronized块中使用,同样地,Condition的await()和signal()方法只能在lock.lock()和lock.unlock()内部使用。

转载于:https://my.oschina.net/Howard2016/blog/1609148

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值