关闭

Condition使用---线程通信

262人阅读 评论(0) 收藏 举报

线程之间除了同步互斥,还要考虑通信。在Java5之前我们的通信方式为:wait 和 notify。那么Condition的优势是支持多路等待,就是我可以定义多个Condition,每个condition控制线程的一条执行通路。传统方式只能是一路等待

 

下面这个例子:启动三个线程分别运行sub1、sub2、sub3,要求是运行完sub1之后再运行完sub2,运行完sub2之后运行sub3,运行完sub3之后重新运行sub1,重复50次。

package hb.condition;

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

public class ConditionCommunication {

	public static void main(String[] args) {

		final Business business = new Business();

		new Thread(new Runnable() {

			public void run() {
				for (int i = 0; i < 50; i++) {
					business.sub1(i);
				}
			}

		}).start();

		new Thread(new Runnable() {

			public void run() {
				for (int i = 0; i < 50; i++) {
					business.sub2(i);
				}
			}

		}).start();

		new Thread(new Runnable() {

			public void run() {
				for (int i = 0; i < 50; i++) {
					business.sub3(i);
				}
			}

		}).start();
	}

	static class Business {
		private int shouldSub = 1;
		private Lock lock = new ReentrantLock();

		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();

		public void sub1(int i) {
			try {
				lock.lock();
				while (shouldSub != 1) {
					try {
						condition1.await();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub1 thread sequence is " + j + " loop of " + i);
				}
				shouldSub = 2;
				condition2.signal();
			} finally {
				lock.unlock();
			}
		}

		public void sub2(int i) {
			try {
				lock.lock();
				while (shouldSub != 2) {
					try {
						condition2.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub2 thread sequence is " + j + " loop of " + i);
				}
				shouldSub = 3;
				condition3.signal();
			} finally {
				lock.unlock();
			}
		}

		public void sub3(int i) {
			try {
				lock.lock();
				while (shouldSub != 3) {
					try {
						condition3.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub3 thread sequence is " + j + " loop of " + i);
				}
				shouldSub = 1;
				condition1.signal();
			} finally {
				lock.unlock();
			}
		}

	}
}

 API说明:

await() : 造成当前线程在接到信号或被中断之前一直处于等待状态。

signal() : 唤醒一个等待线程。

 


 

 我们可以先分析下Java5 Api中的缓冲队列的实现:

假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存put 线程和take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition 实例来做到这一点。

class BoundedBuffer {
	final Lock lock = new ReentrantLock();// 实例化一个锁对象
	final Condition notFull = lock.newCondition(); // 实例化两个condition
	final Condition notEmpty = lock.newCondition();

	final Object[] items = new Object[100];// 初始化一个长度为100的队列
	int putptr, takeptr, count;

	public void put(Object x) throws InterruptedException {
		lock.lock();// 获取锁
		try {
			while (count == items.length)
				notFull.await();// 当计数器count等于队列的长度时,不能在插入,因此等待
			items[putptr] = x; // 将对象放入putptr索引处
			if (++putptr == items.length)
				putptr = 0;// 当索引长度等于队列长度时,将putptr置为0
			// 原因是,不能越界插入
			++count;// 没放入一个对象就将计数器加1
			notEmpty.signal();// 一旦插入就唤醒取数据线程
		} finally {
			lock.unlock();// 最后释放锁
		}
	}

	public Object take() throws InterruptedException {
		lock.lock();// 获取锁
		try {
			while (count == 0)
				// 如果计数器等于0那么等待
				notEmpty.await();
			Object x = items[takeptr]; // 取得takeptr索引处对象
			if (++takeptr == items.length)
				takeptr = 0;// 当takeptr达到队列长度时,从零开始取
			--count;// 每取一个讲计数器减1
			notFull.signal();// 枚取走一个就唤醒存线程
			return x;
		} finally {
			lock.unlock();// 释放锁
		}
	}
}

 

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:326551次
    • 积分:8920
    • 等级:
    • 排名:第2254名
    • 原创:1096篇
    • 转载:14篇
    • 译文:0篇
    • 评论:5条
    最新评论