Java笔记(二十)……线程间通信
概述
当需要多线程配合完成一项任务时,往往需要用到线程间通信,以确保任务的稳步快速运行
相关语句
wait():挂起线程,释放锁,相当于自动放弃了执行权限
notify():唤醒wait等待队列里的第一个线程
notifyAll():唤醒所有等待队列中的线程
他们都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
相关问题
为什么这些操作线程的方法要定义在Object类中
因为这些方法是依赖于锁进行的,而锁又是任意对象,所以这些方法必须定义在Object中,才可以被任意对象的锁调用
为什么使用notifyAll而不是notify
因为notify唤醒的只是等待队列里的第一个线程,该线程不确定,有可能是对方线程,也有可能是本方线程,所以要使用notifyAll来唤醒所有线程,并配合while循环判断标记才能保证运行的正常
实例代码
1: //定义一把枪
2: class Gun
3: {
4: int bullet;
5: boolean isEmpty;
6:
7: Gun()
8: {
9: bullet = 0;
10: isEmpty = true;
11: }
12:
13: //上子弹
14: synchronized void putBullet()
15: {
16: //之所以用while,是因为notifyAll会唤醒所有线程
17: //若唤醒了本方线程,则需要再判断一次条件,确保本方线程不会冲突
18: while(isEmpty != true)
19: {
20: try
21: {
22: wait();
23: }
24: catch (Exception e)
25: {
26: }
27: }
28:
29: bullet+=7;
30: System.out.println("Put bullet : "+ bullet);
31: isEmpty = false;
32: //上满子弹后,唤醒shot线程发射子弹
33: notifyAll();
34: }
35:
36: //射出子弹
37: synchronized void shotBullet()
38: {
39: while(isEmpty != false)
40: {
41: try
42: {
43: wait();
44: }
45: catch (Exception e)
46: {
47: }
48: }
49: System.out.println("Shot bullet : "+bullet--);
50:
51: if(bullet == 0)
52: {
53: isEmpty = true;
54: //子弹打光之后,唤醒put线程继续上子弹
55: notifyAll();
56: }
57:
58: }
59: }
60: class PutBullet implements Runnable
61: {
62: private Gun g;
63: PutBullet(Gun g)
64: {
65: this.g = g;
66: }
67: public void run()
68: {
69: while(true)
70: {
71: g.putBullet();
72: }
73: }
74: }
75:
76: class ShotBullet implements Runnable
77: {
78: private Gun g;
79: ShotBullet(Gun g)
80: {
81: this.g = g;
82: }
83: public void run()
84: {
85: while(true)
86: {
87: g.shotBullet();
88: }
89: }
90: }
91:
92: class MutiThreadDemo
93: {
94: public static void main(String[] args)
95: {
96: Gun g = new Gun();
97: new Thread(new PutBullet(g)).start();
98: new Thread(new ShotBullet(g)).start();
99: new Thread(new PutBullet(g)).start();
100: new Thread(new ShotBullet(g)).start();
101: }
102: }
JDK1.5之后的升级
JDK1.5中提供了多线程的升级解决方案
将同步synchronized替换成Lock操作
将Object中的wait,notify,notifyAll替换成condition对象,该对象可以对Lock锁进行获取
lock_condition机制可以实现只唤醒对方线程,条理更清晰,所以也省去了循环判断标记的动作
代码如下:
1: import java.util.concurrent.locks.*;
2:
3: //定义一把枪
4: class Gun
5: {
6: private int bullet;
7: private boolean isEmpty;
8:
9: private Lock lock = new ReentrantLock();
10:
11: private Condition condition_put = lock.newCondition();
12: private Condition condition_shot = lock.newCondition();
13:
14: Gun()
15: {
16: bullet = 0;
17: isEmpty = true;
18: }
19:
20: //上子弹
21: void putBullet()
22: {
23: lock.lock();
24: try
25: {
26: //之所以用while,是因为notifyAll会唤醒所有线程
27: //若唤醒了本方线程,则需要再判断一次条件,确保本方线程不会冲突
28: if(!isEmpty)
29: condition_put.await();
30:
31: bullet+=7;
32: System.out.println("Put bullet : "+ bullet);
33: isEmpty = false;
34: //上满子弹后,唤醒shot线程发射子弹
35: condition_shot.signal();
36: }
37: catch (InterruptedException e)
38: {
39: }
40: finally
41: {
42: //释放锁的动作一定完成
43: lock.unlock();
44: }
45:
46: }
47:
48: //射出子弹
49: void shotBullet()
50: {
51: lock.lock();
52: try
53: {
54: if(isEmpty)
55: condition_shot.await();
56: System.out.println("Shot bullet : "+bullet--);
57:
58: if(bullet == 0)
59: {
60: isEmpty = true;
61: //子弹打光之后,唤醒put线程继续上子弹
62: condition_put.signal();
63: }
64: }
65: catch (InterruptedException e)
66: {
67: }
68: finally
69: {
70: lock.unlock();
71: }
72:
73: }
74: }
75: class PutBullet implements Runnable
76: {
77: private Gun g;
78: PutBullet(Gun g)
79: {
80: this.g = g;
81: }
82: public void run()
83: {
84: while(true)
85: {
86: g.putBullet();
87: }
88: }
89: }
90:
91: class ShotBullet implements Runnable
92: {
93: private Gun g;
94: ShotBullet(Gun g)
95: {
96: this.g = g;
97: }
98: public void run()
99: {
100: while(true)
101: {
102: g.shotBullet();
103: }
104: }
105: }
106:
107: class MutiThreadDemo2
108: {
109: public static void main(String[] args)
110: {
111: Gun g = new Gun();
112: new Thread(new PutBullet(g)).start();
113: new Thread(new ShotBullet(g)).start();
114: new Thread(new PutBullet(g)).start();
115: new Thread(new ShotBullet(g)).start();
116: }
117: }
118:
停止线程
如何停止线程
只有一种,run方法结束
开启的多线程通常都是循环结构,可以通过修改循环条件来结束run方法
但是当线程挂起时,有时会执行不到循环条件,一直挂起,这样就不会结束,这时需要对冻结状态的线程进行清除
Thread类为我们提供了一种方法,即interrupt()方法,用于解除挂起状态,恢复到运行状态,所以我们既可以改变循环条件,也可以通过处理InterruptedException异常来结束循环,代码如下:
1: import java.util.concurrent.locks.*;
2:
3: //定义一把枪
4: class Gun
5: {
6: private int bullet;
7: private boolean isEmpty;
8:
9: private Lock lock = new ReentrantLock();
10:
11: private Condition condition_put = lock.newCondition();
12: private Condition condition_shot = lock.newCondition();
13:
14: Gun()
15: {
16: bullet = 0;
17: isEmpty = true;
18: }
19:
20: //上子弹
21: void putBullet() throws InterruptedException
22: {
23: lock.lock();
24: try
25: {
26: //之所以用while,是因为notifyAll会唤醒所有线程
27: //若唤醒了本方线程,则需要再判断一次条件,确保本方线程不会冲突
28: if(!isEmpty)
29: condition_put.await();
30:
31: bullet+=7;
32: System.out.println("Put bullet : "+ bullet);
33: isEmpty = false;
34: //上满子弹后,唤醒shot线程发射子弹
35: condition_shot.signal();
36: }
37: finally
38: {
39: //释放锁的动作一定完成
40: lock.unlock();
41: }
42:
43: }
44:
45: //射出子弹
46: void shotBullet() throws InterruptedException
47: {
48: lock.lock();
49: try
50: {
51: if(isEmpty)
52: condition_shot.await();
53: System.out.println("Shot bullet : "+bullet--);
54:
55: if(bullet == 0)
56: {
57: isEmpty = true;
58: //子弹打光之后,唤醒put线程继续上子弹
59: condition_put.signal();
60: }
61: }
62: finally
63: {
64: lock.unlock();
65: }
66:
67: }
68: }
69: class PutBullet implements Runnable
70: {
71: private Gun g;
72: PutBullet(Gun g)
73: {
74: this.g = g;
75: }
76: public void run()
77: {
78: while(true)
79: {
80: try
81: {
82: g.putBullet();
83: }
84: catch (InterruptedException e)
85: {
86: break;
87: }
88:
89: }
90: }
91: }
92:
93: class ShotBullet implements Runnable
94: {
95: private Gun g;
96: ShotBullet(Gun g)
97: {
98: this.g = g;
99: }
100: public void run()
101: {
102: while(true)
103: {
104: try
105: {
106: g.shotBullet();
107: }
108: //对异常进行处理,以退出循环
109: catch (InterruptedException e)
110: {
111: break;
112: }
113: }
114: }
115: }
116:
117: class MutiThreadDemo2
118: {
119: public static void main(String[] args)
120: {
121: Gun g = new Gun();
122: Thread t1 = new Thread(new PutBullet(g));
123: Thread t2 = new Thread(new ShotBullet(g));
124: Thread t3 = new Thread(new PutBullet(g));
125: Thread t4 = new Thread(new ShotBullet(g));
126:
127: t1.start();
128: t2.start();
129: t3.start();
130: t4.start();
131:
132: try
133: {
134: Thread.sleep(5000);
135: }
136: catch (Exception e)
137: {
138: }
139:
140: t1.interrupt();
141: t2.interrupt();
142: t3.interrupt();
143: t4.interrupt();
144: }
145: }
146:
线程类的其他方法
setPriority(int num)
设置线程运行的优先级,效果不绝对,只是个概率问题
setDaemon(boolean b)
守护线程,也叫后台线程,意味着当前台线程结束时,后台线程无论是否挂起,都会退出线程
join()
当A线程执行到B的join方法时,会等待Join方法结束,再继续执行,join方法一般用来临时加入线程操作
toString()
自定义线程名称