Guarded 是被守护,被保卫的意思,Suspension 则是 “暂停的意思”,如果执行现在的处理出现问题,就让执行处理的线程进行等待,这就是 Guarded Suspension 模式,它是通过让线程等待保证实例的安全性
类名 | 说明 |
Request | 表示一个请求类 |
RequestQueue | 依次存放请求的类 |
ClientThread | 发送请求的类 |
ServerThread | 接受请求的类 |
Main | 测试程序 |
用于表示请求,由于只是用于表示 ClientThread 传递给 ServerThread 的实例,所以不用做特殊处理,该类中有一个表示名称的属性(name字段)
@Getter
@Setter
public class Request {
private final String name;
public Request(String name) {
this.name = name;
}
}
该类用于存放请求,定义了 getRequest 和 putRequest 两个方法
getRequest 方法会取出最先存放再 RequestQueue 中的请求,作为其返回值,如果一个请求都没有,那就一直等待,直到其他某个线程执行 putRequest 方法
putRequest 方法用于添加一个请求,当线程需要向 RequestQueue 中添加 Request 实例,可以调用该方法
public class RequestQueue {
private final Queue queue = new LinkedList<>();
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();
}
}
ClientThread 类用于表示发送请求的线程,ClientThread 持有 RequestQueue 的实例,并连续调用该实例的 putRequest,放入请求,为了错开发送的请求时间点,使用random类随机生成了0到100的数字,来作为 sleep 的时间(毫秒)
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);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = new Request("NO." + i);
System.out.println(Thread.currentThread().getName() + "request" + request);
// 请求放入队列
requestQueue.putRequest(request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
//
}
}
}
}
ServerThread 类用于表示接收请求的线程,该类也持有 RequestQueue 的实例,使用 getRequest 方法接收请求,与 ClientThread 一样,ServerThead 也是用随机数进行 sleep
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);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
// 从队列中取出请求
Request request = requestQueue.getRequest();
System.out.println(Thread.currentThread().getName() + "request" + request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
//
}
}
}
}
Main 类首先会创建 RequestQueue 的实例,然后分别创建 ClientThread 和 ServerThread 的实例,并将 requestQueue 传给这两个实例,最后执行 start
public class Main {
public static void main(String[] args) {
RequestQueue queue = new RequestQueue();
new ClientThread(queue, "Alice", 3141592L).start();
new ServerThread(queue, "bili", 2141592L).start();
}
}
运行结果可以看出,名为 Alice 的线程会一直发送请求,而名为 bili 的线程会一直处理请求
Alice, requestRequest(name=NO.0) ALice 发送请求
bili, handlersRequest(name=NO.0) bili 处理请求
Alice, requestRequest(name=NO.1)
bili, handlersRequest(name=NO.1)
Alice, requestRequest(name=NO.2)
bili, handlersRequest(name=NO.2)
Alice, requestRequest(name=NO.3)
bili, handlersRequest(name=NO.3)
Alice, requestRequest(name=NO.4)
bili, handlersRequest(name=NO.4)
Alice, requestRequest(name=NO.5)
bili, handlersRequest(name=NO.5)
Alice, requestRequest(name=NO.6)
仔细查看程序,你会发现 wait/notify/notifyAll 只会出现在 RequestQueue 类中,这种将wait/notify/notifyAll 隐藏起来的做法对 RequestQueue 类的复用性来说是非常重要的,这是因为,使用 RequestQueue 的其他类无需考虑 wait或notify的问题,只需要调用 getRequest 和 putRequest 就行了
jdk1.5的 java.util.concurrent 包中提供了与该 RequestQueue 类功能相同的一个类,那就是 LinkedBlockingQueue类,采用这个类,可以将 RequestQueue 类简化,这里使用的 take 方法和 put 方法都是 BlockingQueue 接口中声明的方法,take 方法取出队列首位的元素,put 方法则用于向队列末尾添加元素,当队列为空是,若调用 take 方法便会进行 wait,由于 take 方法和 put 方法已经考虑可互斥处理,所以不需要使用 syncronized,能够保证线程安全
public class RequestQueue {
private final Queue queue = new LinkedList<>();
private final BlockingQueue blockingQueue = new LinkedBlockingDeque<>();
public Request getRequest() {
Request request = null;
// while (queue.peek() == null) {
// try {
// // 队列为空时,当前线程进入等待队列
// wait();
// } catch (InterruptedException e) {
// //
// }
// }
// // 获取被删除的第一个元素
// return queue.remove();
try {
// 查询源码会发现,当队列为空时, 会进行 wait
request = blockingQueue.take();
} catch (InterruptedException e) {
//
}
return request;
}
public void putRequest(Request request) {
try {
blockingQueue.put(request);
} catch (InterruptedException e) {
//
}
// 唤醒,把等待队列中的线程移出去
// notifyAll();
}
}