虚假唤醒
虚假唤醒
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
class A {
public static void main(String[] args) {
Data data = new Data();
// +1
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
// -1
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
class Data {
private int num = 0;
// +1
public synchronized void increment() throws InterruptedException {
if (num != 0) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "->" + num);
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "->" + num);
this.notifyAll();
}
}
- jdk官方文档
- 当有更多线程时就会出现虚假唤醒,==把if改成while避免虚假唤醒==。==等待应该总是出现在循环中==
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
class A {
public static void main(String[] args) {
Data data = new Data();
// 生产者
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
// 生产者
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
data.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
// 消费者
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(10);
data.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
// 消费者
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(20);
data.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}
class Data {
private int num = 0; // 当前缓冲池中数量
private int maxSize = 3; // 缓冲池最大放3个
// 生产一个
// 关键字在实例方法上,锁为当前实例(当关键字在静态方法上,锁为当前Class对象)
public synchronized void produce() throws InterruptedException {
// 出现虚假唤醒的原因是从阻塞态到就绪态再到运行态没有进行判断,需要让其每次得到操作权时都进行判断
if (num >= maxSize) { // 改成while缓冲池就不会溢出
System.out.println(Thread.currentThread().getName() + "被阻塞在wait()");
this.wait(); // 提前释放synchronized锁,重新去请求锁导致的阻塞
}
num++;
System.out.println(Thread.currentThread().getName() + "生产1个后剩余" + num);
this.notifyAll(); // notify()或者notifyAll()方法并不是真正释放锁,必须等到synchronized方法或者语法块执行完才真正释放锁
}
// 消费1个
public synchronized void consume() throws InterruptedException {
if (num <= 0) { // 改成while缓冲池就不会溢出
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "消费1个后剩余" + num);
this.notifyAll();
}
}
/*
// 如果一开始是消费者抢到锁,由于num=0,会执行wait()。被阻塞在wait(),进入等待队列。
A生产1个后剩余1 // 生产者A先抢到锁,开始生产。(BCD试图竞争锁,都会被阻塞在同步代码块开始处,进入等待队列)
// 生产一个后调用notifyAll()唤醒在等待队列中的消费者CD和生产者B,BCD进入同步队列。
// 同步代码块执行完synchronized会自动释放锁。同步队列中ABCD一起竞争锁。
A生产1个后剩余2 // 又是A抢到锁,BCD还是阻塞在各自同步代码块开始处,进入等待队列。
// 生产一个后notifyAll()唤醒在等待队列中的消费者CD和生产者B,BCD进入同步队列。
// 同步代码块执行完synchronized会自动释放锁。同步队列中ABCD一起竞争锁。
A生产1个后剩余3 // 又是A抢到锁,BCD还是阻塞在各自同步代码块开始处,进入等待队列。
// 生产一个后notifyAll()唤醒在等待队列中的消费者CD和生产者B,BCD进入同步队列。
// 同步代码块执行完synchronized会自动释放锁。同步队列中ABCD一起竞争锁。
// 缓冲区满。若生产者A(B同理)抢到锁,则执行wait(),提前释放锁,自己被阻塞在wait()处,进入等待队列。
// 然后notifyAll()唤醒在等待队列中的消费者CD和生产者B,BCD进入同步队列去竞争锁。
// 如果此时生产者B去竞争锁,也会因为num >= maxSize而调用wait()进入等待队列,这次被阻塞在wait()处。
A被阻塞在wait() // AB都阻塞在wait()
B被阻塞在wait()
C消费1个后剩余2 // 在同步队列中的消费者CD去竞争锁,C抢到,D阻塞在同步代码块开始处,进入等待队列。
// C消费一个后去notifyALl()唤醒了ABD。并在同步代码块结束后释放锁。
// 同步队列中ABCD一起竞争锁。
A生产1个后剩余3 // A抢到锁,BCD阻塞在原来位置(B在wait()处,CD在同步代码块开始处),进入等待队列。
// 生产一个后notifyAll()唤醒在等待队列中的消费者CD和生产者B,BCD进入同步队列。
// 同步代码块执行完synchronized会自动释放锁。同步队列中ABCD一起竞争锁。
A被阻塞在wait() // 缓冲区满。A又抢到了锁,因为num >= maxSize被阻塞在wait()
B生产1个后剩余4 // 生产者B抢到锁,从之前被阻塞的wait()往后执行,不经过if判断,num+1。(此时CD阻塞在各自同步代码块处,A在wait())
// 然后notifyAll()唤醒在等待队列中的消费者CD和生产者A,ACD进入同步队列去竞争锁。
B被阻塞在wait() // B又抢到了锁,因为num >= maxSize被阻塞在wait()。
A生产1个后剩余5 // A抢到锁,从wait()后直接执行,不经过if判断,num+1。
// 然后notifyAll()唤醒在等待队列中的消费者CD和生产者B,ABCD进入同步队列。
// 同步代码块执行完synchronized会自动释放锁。同步队列中ABCD一起竞争锁。
B生产1个后剩余6 // B抢到锁,从之前被阻塞的wait()往后执行,不经过if判断,num+1。(此时ACD都阻塞在同步代码块开始处)
B被阻塞在wait() // B抢到了锁,因为num >= maxSize被阻塞在wait()
// ......
C消费1个后剩余5
B生产1个后剩余6
B被阻塞在wait()
C消费1个后剩余5
B生产1个后剩余6
B被阻塞在wait()
C消费1个后剩余5
B生产1个后剩余6
C消费1个后剩余5
C消费1个后剩余4
C消费1个后剩余3
C消费1个后剩余2
C消费1个后剩余1
C消费1个后剩余0
*/
本文由作者按照 CC BY 4.0 进行授权