一:线程通信介绍
线程通信是通过主动放弃对资源的使用,而让给其它线程的过程。合理的安排多个线程对同一资源的使用,即设计线程间的通信,可以完成很多复杂的任务。
二:线程通信实现
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 }
basics Code
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 }
complete code
参考资料:https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#notify()