Guarded Suspension 模式

概念

Guarded是被守护、被保卫、被保护的意思
Suspension则是“暂停”的意思。如果执行现在的处理会造成问题,就让执行处理的线程进行等
待—这就是 Guarded Suspension模式。
Guarded Suspension模式通过让线程等待来保证实例的安全性。这正如同你让邮递员在门口等待,以保护个人隐私一样。

示例程序

1.类一览表

名字 说明
Request 表示一个请求的类
RequestQueue 一次存放请求的类
ClientThread 发送请求的类
ServerThread 接收请求的类
Main 测试程序行为的类

2.时序图

时序图


3.Request类 (用于表示请求)

1
2
3
4
5
6
7
8
9
10
11
12
public class Request {
private final String name;
public Request(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return "[ Request " + name + " ]";
}
}

4.RequestQueue类

getRequest方法会取出最先存放在 RequestQueue中的一个请求,作为其返回值。如果一个请求都没有,那就一直等待,直到其他某个线程执行 putRequest

putRequest方法用于添加一个请求。当线程想要向 Requestqueue中添加Request实例时,可以调用该方法。总的来说, RequestQueue通过 putrequest放入 Request实例,并按放入顺序使用getRequest取出 Request实例。这种结构通常称为队列( queue)或FFO( First In first out,先进先出)。例如,在银行窗口前依次等待的队伍就是队列的一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Queue;
import java.util.LinkedList;

public class RequestQueue {
private final Queue<Request> queue = new LinkedList<Request>();
public synchronized Request getRequest() {
while (queue.peek() == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
return queue.remove();
}
public synchronized void putRequest(Request request) {
queue.offer(request);
notifyAll();
}
}

5.ClientThread类(用于表示发送请求的线程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Random;

public class ClientThread extends Thread {
private final Random random;
private final RequestQueue requestQueue;
public ClientThread(RequestQueue requestQueue, String name, long seed) {
super(name);
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = new Request("No." + i);
System.out.println(Thread.currentThread().getName() + " requests " + request);
requestQueue.putRequest(request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}

6.ServerThread类(用于表示接收请求的线程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Random;

public class ServerThread extends Thread {
private final Random random;
private final RequestQueue requestQueue;
public ServerThread(RequestQueue requestQueue, String name, long seed) {
super(name);
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = requestQueue.getRequest();
System.out.println(Thread.currentThread().getName() + " handles " + request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}

7.Main类

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
new ClientThread(requestQueue, "Alice", 3141592L).start();
new ServerThread(requestQueue, "Bobby", 6535897L).start();
}
}

8.运行结果
Alice会一直发送请求( requests),而Boby则会不停地处理请求(handles)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Alice requests [ Request No.0 ]  //Alice发送请求 No.0
Bobby handles [ Request No.0 ] //Bobby处理请求No.0
Alice requests [ Request No.1 ]
Alice requests [ Request No.2 ]
Bobby handles [ Request No.1 ]
Bobby handles [ Request No.2 ]
Alice requests [ Request No.3 ]
Bobby handles [ Request No.3 ]
Alice requests [ Request No.4 ]
Bobby handles [ Request No.4 ]
Alice requests [ Request No.5 ]
Alice requests [ Request No.6 ]
Bobby handles [ Request No.5 ]
Bobby handles [ Request No.6 ]
Alice requests [ Request No.7 ]
Bobby handles [ Request No.7 ]
Alice requests [ Request No.8 ]
Bobby handles [ Request No.8 ]
Alice requests [ Request No.9 ]
Bobby handles [ Request No.9 ]
Alice requests [ Request No.10 ]

该模式闪亮登场

GuardedObject:被守护的对象
GuardedObject是一个持有被守护的方法(guardedMethod)的类,当线程执行guardedMethod方法时,若守护条件成立,则可以立刻执行,
当守护条件不成立,就要进行等待。
守护条件的成立与否和被守护对象的状态有关。
所以在上面的示例程序中
RequestQueue:就是被守护对象
getRequest方法:就是被守护方法
putRequest方法:改变状态的方法

wait和notify/notifyAll的责任

上面的示例程序中,我们把wait/notifyAll都写在了RequestQueue类中,并没有出现在ClientThread,ServerThread,Main类中。
Guarded Suspension模式的实现封装在RequestQueue类中。
这种将wait/notifyAll隐藏起来的做法对RequestQueue类的复用性非常重要。当我们在使用RequestQueue时,其他类无需考虑wait,notifyAll的问题,
只要调用getRequest方法或putRequst方法就行。

使用java.util.concurrent.LinkedBlockingQueue的示例程序

这个类和RequestQueue类功能相同。该类中的take方法用于 取出队首元素,put方法则用于向队列末尾添加元素。当队列为空时,若调用take方法便会进行wait,
并且take和put已经考虑了互斥处理。所以getRequest和putRequest方法就无需声明为synchronized了。LinkedBlockingQueue类中使用了Guarded Suspension模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RequestQueue {


private final BlockingQueue<Request> queue = new LinkedBlockingQueue<>();

public void putRequest(Request request){
try {
queue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Request getRequest(){
Request request = null;
try {
request = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return request;
}
}