图解java多线程-Guarded_Suspension_模式

正文

Guarded 是被守护,被保卫,被保护的意思, Suspension 则是暂停的意思.如果执行现在的处理会出现问题,就让处理问题的线程进行等待–这就是 Guarded Suspension 模式

利用 LinkedList 实现 LinkedBlockingQueue?

下面整个 MyBlockQueue api 就是利用了整个模式,当队列为空时,就让 调用 take 的线程一直等待,直到调用 put 后再唤醒

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
package com.creambing.concurrent.chapterthree;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
* FileName: MyBlockQueue
* Author: creambing
* Date: 2019-10-31 11:27
* Description:
* History:
* <author> <time> <version> <desc>
* 作者姓名 修改时间 版本号 描述
*/
public class MyBlockQueue<T> {

private Queue<T> queue = new LinkedList<>();

public synchronized void put(T t) {
queue.add(t);
notifyAll();
}

public synchronized T take() throws InterruptedException {
while (queue.peek() == null) {
wait();
}
return queue.poll();
}

public synchronized int size() {
return queue.size();
}

static class TestQueue implements Runnable {

private final MyBlockQueue<Integer> queue;

public TestQueue(MyBlockQueue<Integer> queue) {
this.queue = queue;
}

@Override
public void run() {
IntStream.range(0, 10).forEach(i -> {
queue.put(i);
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}

public static void main(String[] args) {
MyBlockQueue<Integer> myBlockQueue = new MyBlockQueue<>();
List<Thread> list = IntStream.range(0, 10).mapToObj(i -> new Thread(new TestQueue(myBlockQueue))).collect(Collectors.toList());
list.forEach(Thread::start);
for (Thread thread : list) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("size:"+myBlockQueue.size());
}
}

wait和锁的关系

调用 wait 的时候必须先获取锁
我们先说明一下等待队列,当调用 wait() 方法时,该线程就会放入等待队列中,并同时释放在进入 wait() 方法之前获取的锁
所有实例都拥有一个等待队列,它是在实例的 wait 方法执行后停止操作的线程的队列,打个比方来说,就是为每个实例准备的线程休息室
在执行 wait 方法后,线程便会暂停操作,进入等待队列这个休息室.除非发生下列某一情况,否则线程便会一直在等待队列中休眠.当下列任意一种情况发生时,线程便会退出等待队列

1
2
3
4
有其他线程的 notify 方法来唤醒线程
有其他线程的 notifyAll 方法来唤醒线程
有其他线程的 interrupt 方法来唤醒线程
wait 方法超时

如果 wait 和 notifyAll 需要放在 synchronized 代码块中需要如何改造?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 线程 A 的代码
synchronized(obj_A)
{
while(!condition){
obj_A.wait();
}
// do something
}

// 线程 B 的代码
synchronized(obj_A)
{
if(!condition){
// do something ...
condition = true;
obj_A.notify();
}
}

因为调用 wait() 会挂起该线程同时释放锁,因为 synchronized 代码块中 obj_A 可以时任意对象,所以需要调用该对象的 wait 和 notifyAll 方法
当调用 notifyAll 方法时,此时锁还在 notifyAll 线程上,它并不像 wait 调用后会直接释放锁,它只是通知那些等待队列中的线程,可以退出等待,出来竞争锁了,此时如果 notifyAll 的线程执行完锁中的代码,释放锁之后,那些线程谁获取到锁就继续执行下去
同 wait 方法一样,若要执行 notify 或者 notifyAll 方法,线程必须持有要调用实例的锁

参考资料

Cream Bing wechat
subscribe to my blog by scanning my public wechat account