[线程] 3 线程的通信

线程的通信

Posted by Mr.Vincent on 2019-09-07
Estimated Reading Time 4 Minutes
Words 900 In Total
Viewed Times

假设一个场景

需求:使用两个线程打印 1-100。线程1、线程2交替打印。

涉及到三个方法

  • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  • notify():一旦执行此方法,就会唤醒被 wait() 的一个线程。如果有多个线程被 wait(),就唤醒优先级高的那个。
  • notifyAll():一旦执行此方法,就会唤醒所以被 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
public class WaitNotify {
public static void main(String[] args) {
Number n = new Number();

Thread t1 = new Thread(n);
Thread t2 = new Thread(n);

t1.setName("线程1");
t2.setName("线程2");

t1.start();
t2.start();
}
}

class Number implements Runnable {

private int number = 1;

Object obj = new Object();

@Override
public void run() {
while (true) {
synchronized (obj) {

//线程唤醒
obj.notify();

if (number <= 100) {

try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + ":" + number);
number++;

try {
//线程等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}

说明:

  • wait()notify()notifyAll() 这三个方法必须使用在同步代码块或同步方法中。
  • wait()notify()notifyAll() 这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现 IllegalMonitorStateException 异常。
  • wait()notify()notifyAll() 这三个方法是定义在 java.lang.Object 类中的。

sleep()wait() 的异同

相同点:一旦执行方法,都可以使用当前的线程进入阻塞状态。

不同点:

  • 两个方法声明的位置不同:Thread 类中声明 sleep()Object 类中声明 wait()
  • 调用的要求不同:sleep() 可以在任何需要的场景下调用。wait() 必须使用在同步代码块或同步方法中调用。
  • 关于同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep() 不会释放锁,wait() 会释放锁。
  • sleep() 方法需要抛异常,wait()方法不需要。

线程通讯的应用

经典案例:生产者/消费者

场景:生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员外取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生成更多的产品,店员会叫生产者听一下,如果店中有空位放产品了再通知生产者继续生成;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

解答

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class P_C_Thread {
public static void main(String[] args) {
Clerk clerk = new Clerk();

Producer producer = new Producer(clerk);
producer.setName("生产者1");

Consumer consumer = new Consumer(clerk);
consumer.setName("消费者1");

producer.start();
consumer.start();
}
}


//资源
class Clerk {

private int productCount = 0;

//生成产品
public synchronized void produceProduct() {
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第 " + productCount + " 个产品");

//唤醒
notify();
} else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

//消费产品
public synchronized void consumeProduct() {
if (productCount > 0) {
System.out.println(Thread.currentThread().getName() + ":开始消费第 " + productCount + " 个产品");
productCount--;

//唤醒
notify();
} else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


//生成者
class Producer extends Thread {

private Clerk clerk;

public Producer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
System.out.println(getName() + ":开始生成产品......");

while (true) {

try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

clerk.produceProduct();
}
}
}


//消费者
class Consumer extends Thread {

private Clerk clerk;

public Consumer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
System.out.println(getName() + ":开始消费产品......");

while (true) {

try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

clerk.consumeProduct();
}
}
}

案例源码:https://github.com/V-Vincen/threads


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !