一:线程通信介绍
线程通信是通过主动放弃对资源的使用,而让给其它线程的过程。合理的安排多个线程对同一资源的使用,即设计线程间的通信,可以完成很多复杂的任务。
二:线程通信实现
1,java.lang.Object超类
a,上图为Object类的常用方法,其中提供的notify(),notifyAll(),wait(),wait(long timeout) 和 wait(long timeout,int nanos)五个方法可以实现线程间通信。
b,五个方法都是被final修饰,所以不能被重写。
2,notify方法
notify()使用介绍:Wakes up a single thread that is waiting for this object's monitor.
notifyAll()使用介绍:Wakes up all threads that are waiting for this object's monitor.
3,wait方法
wait()使用介绍:Causes the current thread to wait until another thread invokes the notify() or the notifyAll() method for this object.
wait(long timeout)使用介绍:Causes the current thread to wait until either another thread invokes the notify() or notifyAll() method for this object,or a specified amount of time has elapsed.
wait(long timeout, int nanos)使用介绍:Causes the current thread to wait until another thread invokes the notify() or the notifyAll() method for this object,or some other thread interrupts the current thread ,or a centain amount of the real time has elapsed.
4,个人总结和附录
a,wait后的线程只能通过notify方法唤醒,从而重新进入就绪状态。
b,notify()方法只能唤醒一个在wait的线程,且是随机的。
c,从使用介绍中的 ‘current thread’,解读wait方法的使用需要使用线程必须拥有目标对象的锁,而notify()中使用介绍是主动唤醒其它线程,所以也必须拥有目标对象的锁才有资格去唤醒其它线程。所以wait()和notify方法的使用线程必须拥有目标对象的锁,即在使用时,wait方法和notify方法必须放在synchronized方法或sysnchronized代码块中。
d,wait方法是主动放弃锁,使线程进入等待锁,发生等待阻塞。而sleep方法并没有让线程放弃对象的锁。
附录
wake up:唤醒
monitor:监视器,即lock,锁。
causer:导致,引起
invokes:调用
specified time:指定的时间
elapsed:逝去的
三:设计案例(1010..案例)
1,基础案例
下述案利用合理设计线程间的通信例,实现了1010..的打印。
1 package com.thread.www; 2 /** 3 * 得到 1010的输出结果 4 * @author xiaojia 5 * 6 */ 7 //目标对象 8 class NumberHolder { 9 private int num = 0; 10 //加数 11 public synchronized void increaseNum(){ 12 if(0 != num){ 13 try { 14 wait(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 num ++; 20 System.out.print(num); 21 22 notify(); 23 } 24 //减数 25 public synchronized void decreaseNum(){ 26 if(0 == num){ 27 try { 28 wait(); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 num --; 34 System.out.print(num); 35 notify(); 36 } 37 } 38 39 //加数线程 和 减数线程 40 class IncreaseThread extends Thread{ 41 NumberHolder nholder; 42 43 public IncreaseThread(NumberHolder nholder) { 44 this.nholder = nholder; 45 } 46 47 @Override 48 public void run() { 49 50 for (int i = 0; i < 10; i++) { 51 nholder.increaseNum(); 52 } 53 } 54 } 55 56 class DecreaseThread extends Thread{ 57 NumberHolder nholder; 58 59 public DecreaseThread(NumberHolder nholder) { 60 this.nholder = nholder; 61 } 62 63 @Override 64 public void run() { 65 66 for (int i = 0; i < 10; i++) { 67 nholder.decreaseNum(); 68 } 69 } 70 } 71 72 //启动线程 73 public class Test7线程间的通信1 { 74 75 public static void main(String[] args) { 76 NumberHolder nholder = new NumberHolder(); 77 78 IncreaseThread inTh = new IncreaseThread(nholder); 79 DecreaseThread deTh = new DecreaseThread(nholder); 80 81 inTh.start(); 82 deTh.start(); 83 } 84 85 }
2,问题案例
如果创建多个线程,进行加数和减数,基础案例就得不到1010..有规律的打印了,main方法修改如下:
1 public class Test8线程间的通信2 { 2 3 public static void main(String[] args) { 4 NumberHolder numHolder = new NumberHolder(); 5 6 IncreaseThread inTh = new IncreaseThread(numHolder); 7 DecreaseThread deTh = new DecreaseThread(numHolder); 8 inTh.start(); 9 deTh.start(); 10 11 IncreaseThread inTh2 = new IncreaseThread(numHolder); 12 DecreaseThread deTh2 = new DecreaseThread(numHolder); 13 inTh2.start(); 14 deTh2.start(); 15 } 16 }
3,完整案例
问题案例中,由于有四个创建的线程,拥有锁对象的线程唤醒wait的其它三个线程是随机的,因此不能按规律打印1010..。
解决办法:将加数和减数方法进入wait的if判断修改为while条件,这样当线程被唤醒后还是要进行进行零和非零的判断,才能加数或减数。
1 package com.thread.www; 2 3 class NumberHolder3 { 4 private int num = 0; 5 public synchronized void increaseNum(){ 6 while(num != 0){ 7 try { 8 wait(); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 num ++; 14 System.out.print(num+" "); 15 16 notify(); 17 } 18 19 public synchronized void decreaseNum(){ 20 while(num == 0){ 21 try { 22 wait(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 num --; 28 System.out.print(num+" "); 29 30 notify(); 31 } 32 } 33 34 class IncreaseThread3 extends Thread{ 35 private NumberHolder3 numHolder; 36 public IncreaseThread3(NumberHolder3 numHolder) { 37 this.numHolder = numHolder; 38 } 39 @Override 40 public void run() { 41 for (int i = 0; i < 10; i++) { 42 try { 43 sleep(1000); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 numHolder.increaseNum(); 48 } 49 } 50 } 51 52 class DecreaseThread3 extends Thread{ 53 private NumberHolder3 numHolder; 54 public DecreaseThread3(NumberHolder3 numHolder) { 55 this.numHolder = numHolder; 56 } 57 @Override 58 public void run() { 59 for (int i = 0; i < 10; i++) { 60 try { 61 sleep((long)Math.random()*1000); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 numHolder.decreaseNum(); 66 } 67 } 68 } 69 70 public class Test9线程间的通信3 { 71 72 public static void main(String[] args) { 73 NumberHolder3 numHolder = new NumberHolder3(); 74 75 IncreaseThread3 inTh = new IncreaseThread3(numHolder); 76 DecreaseThread3 deTh = new DecreaseThread3(numHolder); 77 inTh.start(); 78 deTh.start(); 79 80 IncreaseThread3 inTh3 = new IncreaseThread3(numHolder); 81 DecreaseThread3 deTh3 = new DecreaseThread3(numHolder); 82 inTh3.start(); 83 deTh3.start(); 84 85 } 86 87 }
参考资料:https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#notify()
http://www.cnblogs.com/mengdd/archive/2013/02/20/2917956.html