[原创][Java]由一个Java编程思想书中线程间协作的例子想到的
2014-10-11 00:36
330 查看
Java编程思想一书中第21章并发中关于线程间协作的一节中有个关于汽车打蜡与抛光的小例子(原书的704页)。这个例子主要展示的是两个线程如何通过wait与notify/notifyAll方法waxOn的Flag让打蜡线程与抛光线程依次顺序反复运行。其实,本例即使不用synchronized和wait notifyAll而采用sleep忙等的方式,也不会有多线程执行顺序交错的情况出现。改造后的非互斥版本源代码如下:
虽然上面的代码可以实现和书中例子一样的依次执行打蜡,抛光,打蜡,抛光反复动作,在判断waxOn条件处理中采用的“忙等”的方式,运行效率远不如采用wait与notify/notifyAll的方式。
延续上面的思路,我们来看一个多个行程同步执行一系列步骤的情况下,两种策略(采用wait和采用sleep忙等)下的效率的差别。
假定有5个Thread,每个Thread只能执行自己对应的步骤Step
Thread1 执行 Step1
Thread2 执行 Step2
Thread3 执行 Step3
Thread4 执行 Step4
Thread5 执行 Step5
要求必须按照顺序循环往复的执行下去
Step1-Step2-Step3-Step4-Step5-Step1-Step2......
我们编写一个采用了策略模式的小的测试程序,代码如下
sleep时间2微秒时的结果
Begin to test 5 thread cooperation by wait()
Test end. time=94
Begin to test 5 thread cooperation by No wait()
Test end. time=8611
sleep时间1微秒时的结果
Begin to test 5 thread cooperation by wait()
Test end. time=141
Begin to test 5 thread cooperation by No wait()
Test end. time=4969
通过运行结果可以发现
采用wait notifyAll的策略时,所花时间较为随机但是很快。而采用sleep忙等方式时,执行效率和sleep的时间有关联,sleep时间越短
执行越快(sleep(0)除外),可是CPU负荷越大。两者的差值在几十倍左右,可见在多线程进行同步与协作执行任务的时候,采用wait和notifyAll方式而不是
sleep忙等的方式是多么重要。
//: concurrency/waxomatic/WaxOMatic.java // Basic task cooperation. package chapter21.sample; import java.util.concurrent.*; class Car2 { private boolean waxOn = false; public void waxed() { waxOn = true; // Ready to buff ///notifyAll(); } public void buffed() { waxOn = false; // Ready for another coat of wax //notifyAll(); } public void waitForWaxing() throws InterruptedException { while(waxOn == false) TimeUnit.MILLISECONDS.sleep(50); } public void waitForBuffing() throws InterruptedException { while(waxOn == true) TimeUnit.MILLISECONDS.sleep(50); } } class WaxOn2 implements Runnable { private Car2 car; public WaxOn2(Car2 c) { car = c; } public void run() { try { while(!Thread.interrupted()) { System.out.print("Wax On! "); //TimeUnit.MILLISECONDS.sleep(20); car.waxed(); car.waitForBuffing(); } } catch(InterruptedException e) { System.out.println("Exiting via interrupt"); } System.out.println("Ending Wax On task"); } } class WaxOff2 implements Runnable { private Car2 car; public WaxOff2(Car2 c) { car = c; } public void run() { try { while(!Thread.interrupted()) { car.waitForWaxing(); System.out.print("Wax Off! "); //TimeUnit.MILLISECONDS.sleep(20); car.buffed(); } } catch(InterruptedException e) { System.out.println("Exiting via interrupt"); } System.out.println("Ending Wax Off task"); } } public class WaxOMaticNoSynchronized { public static void main(String[] args) throws Exception { Car2 car = new Car2(); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new WaxOff2(car)); exec.execute(new WaxOn2(car)); TimeUnit.SECONDS.sleep(5); // Run for a while... exec.shutdownNow(); // Interrupt all tasks } }
虽然上面的代码可以实现和书中例子一样的依次执行打蜡,抛光,打蜡,抛光反复动作,在判断waxOn条件处理中采用的“忙等”的方式,运行效率远不如采用wait与notify/notifyAll的方式。
延续上面的思路,我们来看一个多个行程同步执行一系列步骤的情况下,两种策略(采用wait和采用sleep忙等)下的效率的差别。
假定有5个Thread,每个Thread只能执行自己对应的步骤Step
Thread1 执行 Step1
Thread2 执行 Step2
Thread3 执行 Step3
Thread4 执行 Step4
Thread5 执行 Step5
要求必须按照顺序循环往复的执行下去
Step1-Step2-Step3-Step4-Step5-Step1-Step2......
我们编写一个采用了策略模式的小的测试程序,代码如下
//: concurrency/waxomatic/WaxOMatic.java // Basic task cooperation. package client; import java.util.concurrent.*; // Step为多个线程用来同步和协作的类 abstract class Step { // 设定总共有几个Step public int allStep; // 记录当前进行到第几个Step public int stepNow; abstract public void doStep(); abstract public void waitForPreStep(int stepNo) throws InterruptedException; } // 采用策略模式,此类为使用互斥与wait/notifyAll算法的Step子类 class StepWithWait extends Step{ @Override // doStep是各个ThreadStep线程执行的方法,执行完之后将stepNow+1表明 // 下一个Step对应的ThreadStep可以执行了。 synchronized public void doStep() { // 当进行到最后一个step的时候,再回到第一个step if( stepNow % allStep == 0 ) stepNow = 1; else stepNow++; notifyAll(); } @Override // 参数stepNo为运行此方法的线程号 // 例如ThreadStep1线程只能在StepNow为1的时候执行,否则通过wait等待 synchronized public void waitForPreStep(int stepNo) throws InterruptedException { while(stepNow != stepNo) wait(); } } //采用策略模式,此类为使用不采用互斥而采用sleep算法的Step子类 class StepNoWait extends Step{ protected long checkInterval; public StepNoWait(long inter) { this.checkInterval = inter; } @Override public void doStep() { if( stepNow % allStep == 0 ) stepNow = 1; else stepNow++; } @Override // 参数stepNo为运行此方法的线程号 // 例如ThreadStep1线程只能在StepNow为1的时候执行,否则通过sleep等待 public void waitForPreStep(int stepNo) throws InterruptedException { while(stepNow != stepNo) TimeUnit.MILLISECONDS.sleep(checkInterval); } } // StepThread类,实现线程的类。 class StepThread implements Runnable { // 定义执行Step的总次数 public static long times = 0 ; // 聚合一个互斥同步的Step对象 private Step step; // 定义当前StepThread是执行第几个Step的Thread private int stepNo; public StepThread(Step step,int stepNo) { this.step = step; this.stepNo = stepNo; } public void run() { try { for(int cnt=0;cnt<times;cnt++){ // 先等待直到轮到自己的Step可以执行的时候 step.waitForPreStep(this.stepNo); /* System.out.print("Step"+this.stepNo+" "); if( step.stepNow % step.allStep == 0 ) System.out.println(); */ // 本Thread执行doStep操作 step.doStep(); } } catch(InterruptedException e) { System.out.println("Exiting via interrupt"); } } } // 用来测试两种策略所花时间的类 public class TestDoStep { public static void main(String[] args) throws Exception { // 测试采用wait/notifyAll算法的情况,总共执行step 1000次 testWait(1000); // 测试采用sleep算法的情况,总共执行1000次,2为忙等时候sleep的时间(微秒) testNoWait(1000,2); } public static void testNoWait(long times,long sleepTime) throws InterruptedException { StepThread.times = times; Step step = new StepNoWait(sleepTime); step.stepNow = 1; step.allStep = 5; System.out.println("Begin to test 5 thread cooperation by No wait()"); long begin = System.currentTimeMillis(); doTest(step); long end = System.currentTimeMillis(); System.out.println("Test end. time="+(end-begin)); } public static void testWait(long times) throws InterruptedException { StepThread.times = times; Step step = new StepWithWait(); step.stepNow = 1; step.allStep = 5; System.out.println("Begin to test 5 thread cooperation by wait()"); long begin = System.currentTimeMillis(); doTest(step); long end = System.currentTimeMillis(); System.out.println("Test end. time="+(end-begin)); } private static void doTest(Step step) throws InterruptedException { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new StepThread(step,1)); exec.execute(new StepThread(step,2)); exec.execute(new StepThread(step,3)); exec.execute(new StepThread(step,4)); exec.execute(new StepThread(step,5)); exec.shutdown(); exec.awaitTermination(1000, TimeUnit.SECONDS); } }
sleep时间2微秒时的结果
Begin to test 5 thread cooperation by wait()
Test end. time=94
Begin to test 5 thread cooperation by No wait()
Test end. time=8611
sleep时间1微秒时的结果
Begin to test 5 thread cooperation by wait()
Test end. time=141
Begin to test 5 thread cooperation by No wait()
Test end. time=4969
通过运行结果可以发现
采用wait notifyAll的策略时,所花时间较为随机但是很快。而采用sleep忙等方式时,执行效率和sleep的时间有关联,sleep时间越短
执行越快(sleep(0)除外),可是CPU负荷越大。两者的差值在几十倍左右,可见在多线程进行同步与协作执行任务的时候,采用wait和notifyAll方式而不是
sleep忙等的方式是多么重要。
相关文章推荐
- 请给一个java线程同步的例子
- 用callback机制使线程的run()方法返回一个值[java net programming这本书中的思想]
- 自制简单的Java下载器——来自《Java高级编程》的一个关于线程的例子(带上部分注释)
- java编程思想笔记-并发之线程协作(四)
- 一个Java线程死锁的例子
- java中什么是线程不安全给出一个例子
- Java编程思想中关于闭包的一个例子
- java编程思想笔记-并发之线程协作(三)
- java中给出一个子线程如何捕获主线程异常的例子
- 自制简单的Java下载器——来自《Java高级编程》的一个关于线程的例子(带上部分注释)
- java线程的一个小例子
- 很简单 很简单的一个java线程 例子
- Java编程思想 之 线程协作
- [原创]一个简单例子解释 Java 工厂模式
- 一个饲养员给动物喂食物的例子体现JAVA中的面向对象思想,接口(抽象类)的用处
- java线程同步问题(一个理解wait()与notify()的例子)
- 一个java 线程 的小例子
- java编程思想之并发(线程之间的协作)
- 一个深入理解JAVA传统线程对象创建的例子
- 一个例子体现JAVA中的面向对象思想,接口(抽象类)的用处