您的位置:首页 > 编程语言 > Java开发

[原创][Java]由一个Java编程思想书中线程间协作的例子想到的

2014-10-11 00:36 330 查看
Java编程思想一书中第21章并发中关于线程间协作的一节中有个关于汽车打蜡与抛光的小例子(原书的704页)。这个例子主要展示的是两个线程如何通过wait与notify/notifyAll方法waxOn的Flag让打蜡线程与抛光线程依次顺序反复运行。其实,本例即使不用synchronized和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忙等的方式是多么重要。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: