wait 和 nofity 在线程中的搭配使用,其实就是生产者消费者的一种应用。
一、为什么要有生产者消费者模式
在实际应使用多线程时,线程间的角色并不一完全一样的,有的线程负责生产数据,有的线程负责消费数据。所在就会有一种情况,就是: 生产者生产数据太快,消费者消费能力跟不上。
比较线程A 不断的new 对象,并将对象放到一个队列里,而线程B,不断的从队列里拿出数据进行逻辑操作。显然线程A new 对象这一操作会更快,如果一直持续下去内存有可能会被撑暴。
解决这个问题的思路之一就是:生产者-消费者模式
二、wait、notify 简单应用示例
Wait 类
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
| public class SimpleWaitTest implements Runnable {
private Object object;
public SimpleWaitTest(Object object) { this.object = object; }
@Override public void run() { testMethod(object); }
public void testMethod(Object lock) { synchronized (lock) { try { System.out.println("testMethod begin " + Thread.currentThread().getName()); lock.wait(); System.out.println("testMethod be notify " + Thread.currentThread().getName()); lock.notify(); } catch (InterruptedException e) { e.printStackTrace(); } } }
public void setObject(Object object) { this.object = object; } }
|
notify 类
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
| public class SimpleNotifyTest implements Runnable {
private Object object;
public SimpleNotifyTest(Object object) { this.object = object; }
@Override public void run() { syncMethod(object); }
public void syncMethod(Object lock) { synchronized (lock) { try { System.out.println("syncMethod begin " + Thread.currentThread().getName()); lock.notify(); lock.wait(); System.out.println("syncMethod be notify " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }
public void setObject(Object object) { this.object = object; } }
|
测试 wait、notify
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Test {
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException { SimpleWaitTest simpleWaitTest = new SimpleWaitTest(object); SimpleNotifyTest simpleNotifyTest = new SimpleNotifyTest(object);
new Thread(simpleWaitTest).start(); Thread.sleep(100); new Thread(simpleNotifyTest).start(); } }
|
三、生产者消费者模式
做用在于生产者执行完生产任务后,阻塞自己再唤醒消费者进行消费。是一种线程间的协作。
下面通过一个例子,打印奇偶数来看这一过程。
效果:交替打印奇数 和 偶数
1.设计
- 生产类
- 消费类
- 公共标识符,独立于生产为 和 消费类
为什么要分开生产类 和 消费类,因为如果业务相同的线程,使用线程池就可以完成,不需要这么麻烦,控制两个线程的频率。
两边代码几乎相同,不同的就是 flag 的初始值不相同,为了在程序启动时,一个线程先执行,别一个线程直接进行 wait 状态,等待唤醒。
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
| public class TwoThreadWaitNotify { private int start = 1; private boolean flag = false; public static void main(String[] args) { TwoThreadWaitNotify twoThread = new TwoThreadWaitNotify(); Thread t1 = new Thread(new OuNum(twoThread)); t1.setName("线程-A");
Thread t2 = new Thread(new JiNum(twoThread)); t2.setName("线程-B");
t1.start(); t2.start(); }
public static class OuNum implements Runnable { private TwoThreadWaitNotify number; public OuNum(TwoThreadWaitNotify number) { this.number = number; } @Override public void run() { while (number.start <= 100) { synchronized (TwoThreadWaitNotify.class) { System.out.println("偶数线程抢到锁了"); if (number.flag) { System.out.println(Thread.currentThread().getName() + "-->偶数" + number.start); number.start++; number.flag = false; TwoThreadWaitNotify.class.notify(); }else { try { TwoThreadWaitNotify.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
public static class JiNum implements Runnable { private TwoThreadWaitNotify number; public JiNum(TwoThreadWaitNotify number) { this.number = number; } @Override public void run() { while (number.start <= 100) { synchronized (TwoThreadWaitNotify.class) { System.out.println("奇数线程抢到锁了"); if (!number.flag) { System.out.println(Thread.currentThread().getName() + "-->奇数" + number.start); number.start++; number.flag = true; TwoThreadWaitNotify.class.notify(); }else { try { TwoThreadWaitNotify.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } }
|