wait 和 notify 是object类的方法 ,也就是说在java世界里,所有的class对象都会持有这2个方法。
在使用过程中,wait 和 notify一定是成对出现的。
在调用wait时候,线程必须持有被调用对象的锁。当wait被调用,线程就会释放持有的对象锁 。 sleep方法并不会释放锁。
关于wait 和 notify方法的总结:
- 调用wait时候,首先要保证线程是有了对象的锁
- 调用了wait后,线程释放当前对象锁,并且进入等待
- 进入等待状态后,会等待其他线程调用 notify或者notifyAll 进行唤醒
- 当线程被唤醒后,就会和线程一起竞争对象锁(公平竞争),只有当线程获取到锁,才会继续执行
- 调用wait的代码需要放在 synchronize 方法或者 synchronize代码块中,保证代码在调用wait时候,已经获取锁。
- 当调用notify方法时候,会随机唤醒正在等待唤醒线程集合中的一个,一旦获取到锁,才会继续往下执行
- 在某一时刻,只会有一个线程会持有对对象锁
需求: 使用线程按照101010…..这种顺序打印
下面的代码实现中,利用 wait和 notify ,在2线程中分别进行数据的加和减
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| public class Test01 {
public static void main(String[] args) {
Bucket bucket = new Bucket(); new Thread(new Product(bucket) , "product - 1").start(); new Thread(new Consumer(bucket), "consumer - 1").start(); } }
class Bucket {
private int count = 0 ;
public synchronized void getCount() { if (count != 1) { try { System.out.println("线程: " + Thread.currentThread().getName() + "进入等待 "); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; System.out.println("线程: " + Thread.currentThread().getName() + " count = " + count); notify(); }
public synchronized void setCount() { if (count!= 0) { //wait try { System.out.println("线程: " + Thread.currentThread().getName() + "进入等待 "); wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
count++; System.out.println("线程: " + Thread.currentThread().getName() + " count = " + count); notify(); } }
class Consumer implements Runnable {
private final Bucket bucket;
public Consumer(Bucket bucket) { this.bucket = bucket; }
@Override public void run() { while (true) { try { Thread.sleep(new Random().nextInt(1000) + 100); } catch (InterruptedException e) { e.printStackTrace(); } bucket.getCount(); } } }
class Product implements Runnable {
private final Bucket bucket;
public Product(Bucket bucket) { this.bucket = bucket; }
@Override public void run() { while (true) { try { Thread.sleep(new Random().nextInt(1000) + 100); } catch (InterruptedException e) { e.printStackTrace(); } bucket.setCount(); } } }
|
执行结果,如预期 :
线程: consumer - 1进入等待
线程: product - 1 count = 1
线程: consumer - 1 count = 0
线程: product - 1 count = 1
线程: consumer - 1 count = 0
线程: product - 1 count = 1
下面我们对程序做一次改造 ,在main方法里面 各自使用2个线程进行操作。
1 2 3 4 5 6 7 8 9 10 11
| public static void main(String[] args) {
Bucket bucket = new Bucket();
new Thread(new Product(bucket) , "product - 1").start(); new Thread(new Product(bucket) , "product - 2").start();
new Thread(new Consumer(bucket), "consumer - 1").start(); new Thread(new Consumer(bucket), "consumer - 2").start(); }
|
执行结果:
线程: product - 2 count = 1
线程: consumer - 2 count = 0
线程: consumer - 2进入等待
线程: consumer - 1进入等待
线程: product - 1 count = 1
线程: consumer - 2 count = 0
线程: consumer - 1 count = -1
线程: product - 2进入等待
线程: product - 1进入等待
线程: consumer - 2进入等待
线程: consumer - 1进入等待
......
线程进入了挂起状态,也不再打印结果
在使用了多个线程以后,我们的程序执行结果失控了 。。。
下面我们对2种情况进行分析 ,首先是单个线程:
在Bucket中 ,我们调用了wait和notify ,因为只有2个线程,所以当我们其中一个线程进入wait,另外一个线程一定会被唤醒 ,所以 product-1 和 consum-1 有序的进行增加和减少,保证了我们的运行结果 。
在多个product和consume线程的模式下,
- 担当product-1获取到锁,如果满足 count != 0 那么进入wait,唤醒其他等待队列中的任意一个线程。
- 假如product-2被唤醒,这时候也满足了 count!=0 这个条件,product-2也进入wait
- 这时候等待队列中的任意一个被欢迎,如果是product-1 那么继续往下执行,count++,并唤醒等待队列中的线程
- product-2被唤醒,也执行 count++ ,那么就出现了我们上面失控的结果
解决方案:
1 2 3 4 5
| public synchronized void getCount() { if (count != 1) { public synchronized void getCount() { while (count != 1) {
|
将 if 修改为while , 让程序获取到锁以后,再次检查count的状态即可