`

并发编程回顾:线程协作与Condition接口

 
阅读更多

原先多线程并发编程的学习笔记和代码整理一下贴上来。

---------------------------------

线程协作与Condition接口

一、线程协作

Java提供的线程之间协作方式主要有两种:

1、Object类中提供的wait、notify、notifyAll:

这种方式历史悠久,本篇只简单介绍一下。

相关知识点:

(1)wait()等待某个条件变化,这种条件将由另一个线程来改变,但不能因为测试这个条件而不断的空循环。所以wait()会在等待外部循环的时候挂起,并且只有在notify(),notifyAll()发生时,才会唤醒继续执行。

(2)必须从同步环境中调用wait()\notify()\notifyAll()。
在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。
当调用notify()时,并不意味着这时线程会放弃其锁,如果线程仍旧在完成同步代码,则线程在移出同步代码之前不会放弃锁,所以只调用notify()不意味着这时该锁将变得可用。
可以使用notifyAll()让所有线程冲出等待区,返回可运行状态。多个线程等待一个对象,使用notify()将只影响其中一个,具体哪个将由JVM决定。
(3)当使用wait()\notify()\notifyAll()时,几乎总有一个while(condition)循环包围着wait(),以便检查条件,并强制连续等待,直到条件满足为止。让时间到期,也可以令wait恢复执行,即使代码没有调用notify()或notifyAll()。因此,将wait()放入while循环,并且重新检查表示我们正在等待的事情的条件。我们确保不管因为什么原因醒来,当且仅当我们正在等待的事情还没有发生,将重新进入wait()。

2、javase5开始新加入的java.util.concurrent.locks包中Condition接口:

下面会重点介绍新接口的使用。

二、Condition接口

javase5新加入的java.util.concurrent.locks包中提供了Condition接口,按照JDK文档的描述:

Condition将Object监视器方法(wait、notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。

Condition实现可以提供不同于Object监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁

下面是一个示例:

这是一个给汽车涂蜡和抛光的例子,忘了当时是看了网上哪篇文章还是看了哪本书,觉得不错就记录下来了。

Condition使用起来与Object监视器方法比较类似,先定义一个汽车类:

class Car{
	private boolean waxOn = false;
	private Lock lock = new ReentrantLock();
	private Condition cond = lock.newCondition();
	
	public void buffed(){
		lock.lock();
		try{
			waxOn=false;
			cond.signalAll();//
		}finally{
			lock.unlock();
		}
	}
	public void waxed(){
		lock.lock();
		try{
			waxOn=true;
			cond.signalAll();//
		}finally{
			lock.unlock();
		}
	}
	public void waitingForWax() throws InterruptedException{
		lock.lock();
		try{
			while(waxOn==false){//汽车在抛光时一直等待
				cond.await();
			}
		}finally{
			lock.unlock();
		}
	}
	public void waitingForBuffing() throws InterruptedException{
		lock.lock();
		try{
			while(waxOn==true){//汽车在涂蜡时一直等待
				cond.await();
			}
		}finally{
			lock.unlock();
		}
	}
}

该类使用了ReentrantLock,并通过lock.newCondition();获取一个Condition。状态waxOn代表该车是否涂蜡,涂蜡之后抛光,因此waxed和buffed分别是涂蜡后和抛光后改变waxOn状态的操作。waitingForWax和waitingForBuffing代表等待涂蜡和等待抛光。 

然后定义一个涂蜡任务:

class WaxOn implements Runnable{
	private Car car;
	public WaxOn(Car c){
		this.car=c;
	}
	@Override
	public void run() {
		try{
			while(true){
				System.out.println("waxOn!");
				car.waxed();
				car.waitingForBuffing();
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("Ending WaxOn task.");
	}
}
这个任务一直循环给汽车涂蜡,不过每次涂蜡前要等汽车抛光完成之后再涂。

然后继续定义一个抛光任务:

class WaxOff implements Runnable{
	private Car car;
	public WaxOff(Car c){
		this.car=c;
	}
	@Override
	public void run(){
		try{
			while(true){
				car.waitingForWax();
				System.out.println("waxOff!");
				car.buffed();
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("Ending WaxOff task.");
	}
}

该任务循环给汽车抛光,但抛光前需要先等涂蜡结束,然后再抛光。

最后,开始给汽车抛光和涂蜡了吐舌头

Car car=new Car();
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new WaxOff(car));
es.execute(new WaxOn(car));

控制台输出:

waxOn!
waxOff!
waxOn!
waxOff!
waxOn!
waxOff!
......

两个任务会循环不停的给汽车涂蜡和抛光,除非你手工停止这个程序。同时,涂蜡和抛光操作又不会同时进行,总是会等待另一个操作结束之后才开始,实现了2个线程的协作。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics