原先多线程并发编程的学习笔记和代码整理一下贴上来。
---------------------------------
线程协作与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个线程的协作。