节录于<<张孝祥-Java多线程与并发库高级应用>>
在看这篇文章之前可以先看看
题目概述
主线程执行10次 然后子线程执行5次 接着主线程再执行10次 子线程执行5次....一直循环50次。
首先我们应该明白我们的线程逻辑在于
"主线程执行10次 然后子线程执行5次"
至于循环50次不是我们最核心的业务逻辑。
因而我们先把"主线程执行10次 然后子线程执行5次"放到一个类中。
第一步
class Business{
public void main(int j){ //j作为最大的那个50次循环
for(int i=1;i<=10;i++)
System.out.println("main "+i+" of loop "+j);
}
public void sub(int j){ //j作为最大的那个50次循环
for(int i=1;i<=5;i++)
System.out.println("sub "+i+" of loop "+j);
}
}
然后就简单了,我们做出调用的类
public class TraditionalCommunication{
public static void main(String[] args){
final Business b=new Business();
new Thread(
new Runnable(){
public void run(){
for(int i=1;i<=50;i++)
b.sub(i);
}
}
).start();
for(int i=1;i<=50;i++)
b.main(i);
}
}
如果就是现在这个代码去运行。看到的结果肯定是杂乱无章的。
可能父线程刚走了一圈(输出了一行代码),子线程就抢过了执行权。
第二步
所以只是我们得给business的两个方法,main与sub加上synchronized。
这个时候再测试,最起码两个线程(父线程子线程)不会互相干扰(互斥),但是还有可能父线程接连着运行了两圈(输出了20行代码)也就是说,我们让两个线程能互斥,但是不能协调的通信。(他们没有交替输出!)
第三步
class Business{
private boolean shouldsub=true; //这个变量名 起的很不错
public synchronized void main(int j){ //j作为最大的那个50次循环
if(shouldsub){
try{
this.wait();
}catch(Exception e){ }
}
for(int i=1;i<=10;i++)
System.out.println("main "+i+" of loop "+j);
shouldsub=true; //父线程已经执行了一次 所以shouldsub为true 该子线程执行了
this.notify();
}
public synchronized void sub(int j){ //j作为最大的那个50次循环
if(!shouldsub){
try{
this.wait();
}catch(Exception e){ }
}
for(int i=1;i<=5;i++)
System.out.println("sub "+i+" of loop "+j);
shouldsub=false; //子线程已经执行了一次 所以shouldsub为false 不能再执行了
this.notify();
}
}
这下OK了。大家看看还有可以修改的地方么?
if(!shouldsub){
try{
this.wait();
}catch(Exception e){ }
}
//应该改成下面的形式
while(!shouldsub){
try{
this.wait();
}catch(Exception e){}
}
为什么?
因为有的时候线程会被"假唤醒",用while循环可以再判断一回。
其实写完代码,我仍然觉得这个题的关键就是 把business类抽象出来,提炼出真正核心的业务逻辑。