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());
//1. 锁住自己
lock.wait();
System.out.println("testMethod be notify " + Thread.currentThread().getName());
//4.唤醒 wait 线程
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());
//2.唤醒 wait 线程后 wait
lock.notify();
//3.wait 住,等待唤醒
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
/**
* Created by kayle on 2016/12/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.设计

  1. 生产类
  2. 消费类
  3. 公共标识符,独立于生产为 和 消费类

为什么要分开生产类 和 消费类,因为如果业务相同的线程,使用线程池就可以完成,不需要这么麻烦,控制两个线程的频率。
两边代码几乎相同,不同的就是 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();
}
}
}
}
}
}
}