多线程之Guarded Suspension 模式

Guarded 是被守护,被保护的意思,Suspension 是暂停的意思。如果执行现在的处理会造成问题,就让执行处理的线程进行等待。

创建5个类

名字说明
Request

表示一个请求的类

RequestQueue依次存放请求的类
ClientThread发送请求的类
ServerThrad接受请求的类
Main测试类
/*** 虽说是请求,由于只是用于表示clientThread传递给ServerThread的实例,所以不提供什么特殊的处理*/
public class Request {private  final  String name;public Request(String name) {this.name = name;}public String getName() {return name;}@Overridepublic String toString() {return "Request{" +"name='" + name + '\'' +'}';}
}

import java.util.LinkedList;
import java.util.Queue;/*** 此类用于依次存放请求,RequestQueue通过putRequest放入Request实例,并按顺序使用getRequest取出Request实例。* 这种结构通常称为队列(queue)或者 FIFO(First In First Out ,先进先出)**/
public class RequestQueue {private final Queue queue = new LinkedList<>();// 取出最先放在RequestQueue的中的一个请求,作为返回值,如果一个请求也没有就一直等待,直到其他线程执行putRequest.public synchronized Request getRequest() {while (queue.peek() == null) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}return queue.remove();}}// 添加一个请求public  synchronized void putRequest(Request request){queue.offer(request);notifyAll();}
}
import java.util.Random;/*** 表示发送请求的线程。ClientThread持有RequestQueue的实例,并连续调用该实例的putRequest,放入请求。* */
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);}@Overridepublic 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 {// 为了错开发送请求的执行点,使用java.util.Random类随机生成0-1000之间的数Thread.sleep(random.nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}}}
}
import java.util.Random;/*** 用于表示接收请求的线程,该类也持有RuquestQueue的实例,ServerThread使用该实例的getRequst方法来接受请求。*/
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);}@Overridepublic 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) {e.printStackTrace();}}}
}

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();}
}

运行结果:Alice源源不断的发送请求,而Bobby则会不停的处理请求。

 

在看RequestQuene类代码前,先看看

private final Queue queue = new LinkedList<>();
LinkedList类表示链表结构的集合。实例中用了该类的如下三个方法:
  1. Request remove() : 表示用于移除队列的第一元素,并返回该元素。如果队列中一个元素都没有,则会抛出异常
  2. boolean offer(Request req): 将元素req添加到队列末尾。
  3. Request peek(): 如果队列中存在元素,则该方法会返回头元素,如果为空,则返回null。该方法不移除元素。

 LinkedList是线程不安全的,java.util.concurrent.LinkedBlockingQueue是线程安全的。

先看看获取请求

public synchronized Request getRequest() {while (queue.peek() == null) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}return queue.remove();
}

想要获取个请求 ,那这个请求必须存在,如果没有请求,那你就给我等着,别动不动就来要请求。

queue.peek()  != null 即为守护条件。满足才能 queue.remove();

while() 里面就是守护条件的逻辑非判断,如果满足,不好意思。只能wait!!!

 

再看看添加请求

public  synchronized void putRequest(Request request){queue.offer(request);notifyAll();
}

执行 queue.offer(request)就是向队列中的尾部添加一个请求。

这时队列中至少存在一个可取出的元素,因此可以在上一个方法中可以成功取出请求,就别让他等了,赶紧notifyAll()唤醒吧!

当使用synchronized关键字的时候你要想清楚它需要保护什么,在这里它保护的就是这个队列的实例queue,而这两个方法对队列的操作()应该保证同一时刻只有一个线程执行。

这里提醒一点,某个线程执行某个实例的wait方法,这时线程必须获取该实例的锁,上面调用wait时,其实就已经提前获取到了this对象的锁。线程执行this的wait方法后。进入this的等待队列,并释放持有的this锁。

notify、notifyAll、interrupt会让线程退出等待队列,但是在实际地继续执行处理之前,该线程也还会检查守护条件。不管被notify/notifyAll多少次,如果守护条件不成立,线程都会随着while再次wait(还必须再获取this的锁)

 

Guarded Suspension 模式中的登场角色


GuardedObject(被守护的对象)

GuardedObject角色是一个持有被守护的方法(guardedMethod)的类,当线程执行guardedMethod方法时,若守护条件成功,则可以立即执行操作;不成立时,就要进行等待。除了guardedMethod方法,GuardedObject角色还应该有持有其他改变实例状态的方法(stateChangingMethod)。stateChangingMethod通过notify/notifyAll方法来实现,guardedMethod通过while语句和wait方法来实现。

 

Guarded Suspension 模式的特点是:

  • 存在循环
  • 存在条件检查
  • 因为某种原因而等待

 

重构:使用java.util.concurrent.LinkedBlocklingQueue替换RequestQueue类中的操作方式

 

LinkedBlocklingQueue是线程安全的类,因此重构后的代码如下:
public class RequestQueue {private final BlockingQueue queue = new LinkedBlockingQueue<>();// 取出最先放在RequestQueue的中的一个请求,作为返回值,如果一个请求也没有就一直等待,直到其他线程执行putRequest.public  Request getRequest() {Request request = null;try {request = queue.take(); // 取出队首元素,如果队列为空,会进行等待} catch (InterruptedException e) {e.printStackTrace();}return request;}// 添加一个请求public  void putRequest(Request request){try {queue.put(request); // 向队尾添加元素} catch (InterruptedException e) {e.printStackTrace();}}
}

 

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部