传统线程同步通信技术

先看一个问题:

有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。

看完这个问题,很明显要用到线程间的通信了, 先分析一下思路:首先肯定要有两个线程,然后每个线程中肯定有个50次的循环,因为每个线程都要往返执行任务50次,主线程的任务是执行5次,子线程的任务是执行10次。线程间通信技术主要用到 wait() 方法和 notify() 方法。wait() 方法会导致当前线程等待,并释放所持有的锁,notify() 方法表示唤醒在此对象监视器上等待的单个线程。下面来一步步完成这道线程间通信问题。

首先不考虑主线程和子线程之间的通信,先把各个线程所要执行的任务写好:

public class TraditionalThreadCommunication { public static void main(String[] args) { //开启一个子线程 new Thread(new Runnable() { @Override public void run() { for(int i = 1; i <= 50; i ++) { synchronized (TraditionalThreadCommunication.class) { //子线程任务:执行10次 for(int j = 1;j <= 10; j ++) { System.out.println("sub thread sequence of " + j + ", loop of " + i); } } } } }).start(); //main方法即主线程 for(int i = 1; i <= 50; i ++) { synchronized (TraditionalThreadCommunication.class) { //主线程任务:执行5次 for(int j = 1;j <= 5; j ++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } } } } }

如上,两个线程各有50次大循环,执行50次任务,子线程的任务是执行10次,主线程的任务是执行5次。为了保证两个线程间的同步问题,所以用了 synchronized 同步代码块,并使用了相同的锁:类的字节码对象。这样可以保证线程安全。但是这种设计不太好,就像我在上一节的死锁中写的一样,我们可以把线程任务放到一个类中,这种设计的模式更加结构化,而且把不同的线程任务放到同一个类中会很容易解决同步问题,因为在一个类中很容易使用同一把锁。所以把上面的程序修改一下:

public class TraditionalThreadCommunication { public static void main(String[] args) { Business bussiness = new Business(); //new一个线程任务处理类 //开启一个子线程 new Thread(new Runnable() { @Override public void run() { for(int i = 1; i <= 50; i ++) { bussiness.sub(i); } } }).start(); //main方法即主线程 for(int i = 1; i <= 50; i ++) { bussiness.main(i); } } } //要用到的共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。 class Business { public synchronized void sub(int i) { for(int j = 1;j <= 10; j ++) { System.out.println("sub thread sequence of " + j + ", loop of " + i); } } public synchronized void main(int i) { for(int j = 1;j <= 5; j ++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } }

经过这样修改后,程序结构更加清晰了,也更加健壮了,只要在两个线程任务方法上加上 synchronized 关键字即可,用的都是 this 这把锁。但是现在两个线程之间还没有通信,执行的结果是主线程循环执行任务50次,然后子线程再循环执行任务50次,原因很简单,因为有 synchronized 同步。

下面继续完善程序,让两个线程之间完成题目中所描述的那样通信:

public class TraditionalThreadCommunication { public static void main(String[] args) { Business bussiness = new Business(); //new一个线程任务处理类 //开启一个子线程 new Thread(new Runnable() { @Override public void run() { for(int i = 1; i <= 50; i ++) { bussiness.sub(i); } } }).start(); //main方法即主线程 for(int i = 1; i <= 50; i ++) { bussiness.main(i); } } } //要用到共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高雷剧和程序的健壮性。 class Business { private boolean bShouldSub = true; public synchronized void sub(int i) { while(!bShouldSub) { //如果不轮到自己执行,就睡 try { this.wait(); //调用wait()方法的对象必须和synchronized锁对象一致,这里synchronized在方法上,所以用this } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j = 1;j <= 10; j ++) { System.out.println("sub thread sequence of " + j + ", loop of " + i); } bShouldSub = false; //改变标记 this.notify(); //唤醒正在等待的主线程 } public synchronized void main(int i) { while(bShouldSub) { //如果不轮到自己执行,就睡 try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j = 1;j <= 5; j ++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } bShouldSub = true; //改变标记 this.notify(); //唤醒正在等待的子线程 } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzzfd.html