[Java Concurrent] 多线程合作 wait / notifyAll 的简单案例
本案例描述的是,给一辆汽车打蜡、抛光的场景。
Car 是一辆被打蜡抛光的汽车,扮演共享资源的角色。
WaxOnCommand 负责给汽车打蜡,打蜡时需要独占整部车,一次打一部分蜡,等待抛光,然后再打一部分蜡。
BuffCommand 负责给汽车抛光,抛光时需要独占整部车,每一次会将刚打上的蜡抛光,然后等待打蜡,然后再将刚打上的蜡抛光。
WaxOnCommand 、BuffCommand 分别由两个线程相互交替地执行:
WaxOnCommand 打蜡、等待抛光,BuffCommand 抛光,等待打蜡;
WaxOnCommand 打蜡、等待抛光,BuffCommand 抛光,等待打蜡;
WaxOnCommand 打蜡、等待抛光,BuffCommand 抛光,等待打蜡;
......
CarWaxBuffDemo 演示这个场景。
下面是类之间的关系图。
由于 ExecutorServices 的使用和 Command Pattern 很相似,所以参照其 UML 绘制而成。
代码实现:
Car, 可以改变汽车的打蜡状态,可以等待打蜡状态的改变。输出内容到控制台时,会把当前执行线程一一同输出。
public class Car { private boolean isWaxed = false; public synchronized void waxed(){ isWaxed = true; print(" car is waxed now "); notifyAll(); } public synchronized void unwaxed(){ isWaxed = false; print(" car is unwaxed now "); notifyAll(); }public synchronized void waitingForReWax() throws InterruptedException{ while (isWaxed == true){ wait(); } print(" OK to re-wax "); } public synchronized void waitingForReBuff() throws InterruptedException{ while (isWaxed == false){ wait(); } print(" OK to re-Buff "); } public void print(String msg){ System.out.println("[" + Thread.currentThread().getName() + " ] " + msg); } }
WaxonComman,给汽车打蜡,主要是两个动作:1.等待可以打蜡,2.打蜡。
import java.util.concurrent.TimeUnit; public class WaxonCommand implements Runnable { private Car car; public WaxonCommand(Car car){ this.car = car; } @Override public void run() { try { while (true){ car.waitingForReWax(); print(" WaxOn take action "); TimeUnit.MILLISECONDS.sleep(100); car.waxed(); } }catch (Exception e) { e.printStackTrace(); } try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } print(" Ending WaxOn action "); } public void print(String msg){ System.out.println("[" + Thread.currentThread().getName() + " ] " + msg); } }
buffCommand, 给汽车抛光,主要两个动作:1.等待可以抛光,2. 抛光。
import java.util.concurrent.TimeUnit; public class BuffCommand implements Runnable{ private Car car; public BuffCommand(Car car){ this.car = car; } @Override public void run() { try { while (true){ car.waitingForReBuff(); print(" Buff take action "); TimeUnit.MILLISECONDS.sleep(80); car.unwaxed(); } }catch (InterruptedException e) { e.printStackTrace(); } try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } print(" Ending Buff action "); } public void print(String msg){ System.out.println("[" + Thread.currentThread().getName() + " ] " + msg); } }
演示给汽车交替打蜡、抛光的场景。给 1 秒的时间,让 WaxonCommand / BuffComand 线程进行打蜡、抛光操作,1 秒后,中断他们的操作。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class CarWaxBuffDemo { public static void main() throws InterruptedException{ ExecutorService exec = Executors.newCachedThreadPool(); Car car = new Car(); exec.execute(new BuffCommand(car)); exec.execute(new WaxonCommand(car)); TimeUnit.SECONDS.sleep(1); System.out.println(" executor stopping "); exec.shutdownNow(); } }
输出结果:
在被强制中断前,thread-1 和 thread-2 交替进行打蜡、抛光操作。
[pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action [pool-1-thread-1 ] car is waxed now [pool-1-thread-2 ] OK to re-Buff [pool-1-thread-2 ] Buff take action [pool-1-thread-2 ] car is unwaxed now [pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action [pool-1-thread-1 ] car is waxed now [pool-1-thread-2 ] OK to re-Buff [pool-1-thread-2 ] Buff take action [pool-1-thread-2 ] car is unwaxed now [pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action [pool-1-thread-1 ] car is waxed now [pool-1-thread-2 ] OK to re-Buff [pool-1-thread-2 ] Buff take action [pool-1-thread-2 ] car is unwaxed now [pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action [pool-1-thread-1 ] car is waxed now [pool-1-thread-2 ] OK to re-Buff [pool-1-thread-2 ] Buff take action [pool-1-thread-2 ] car is unwaxed now [pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action [pool-1-thread-1 ] car is waxed now [pool-1-thread-2 ] OK to re-Buff [pool-1-thread-2 ] Buff take action [pool-1-thread-2 ] car is unwaxed now [pool-1-thread-1 ] OK to re-wax [pool-1-thread-1 ] WaxOn take action executor stopping java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at concurrencyCooperation.Car.waitingForReBuff(Car.java:29) at concurrencyCooperation.BuffCommand.run(BuffCommand.java:17) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at concurrencyCooperation.WaxonCommand.run(WaxonCommand.java:20) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) [pool-1-thread-2 ] Ending Buff action [pool-1-thread-1 ] Ending WaxOn action
参考资料 :
Page 857, wait() and notifyAll(), Thinking in Java