java多线程之Guarded Suspension模式
Guarded Suspension模式(等我准备好哦)
在Single Threaded Execution模式中,只要有一个线程进入临界区,其他线程就无法进入,只能等待。而在Guarded Suspension模式中,线程是否等待取决于守护条件。Guarded Suspension模式是在Single Threaded Execution模式的基础上附加了条件而形成的。
如下一段代码实例:
request用于表示请求,表示ClientThread传递给ServerThread的实例
public class Request {
private final String name;
public Request(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public String toString(){
return "[Request"+ name+"]";
}
}
RequestQueue类用于依次存放请求,类中定义了getRequest方法和putRequest方法
getRequest方法会取出最先存放在requestQueue中的一个请求,作为其返回值。如果一个请求都没有,那么就一直等待,直到其他线程执行putRequest
putRequest方法用于添加一个请求。当线程想要向RequestQueue中添加Request实例时,可以调用该实例方法。
public class RequestQueue {
private final Queue<Request> queue = new LinkedList<Request>();
public synchronized Request getRequest(){
while(queue.peek()==null){//守护条件
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return queue.remove();
}
public synchronized void putRequest(Request request){
queue.offer(request);
notifyAll();
}
}
CientThread 类用于表示发送请求的线程。ClientThread持有RequestQueue的实例,并连续调用该实例的putRequest,放入请求,请求的名称依次为NO1,NO2.....
为了错开发送请求的时间点,使用random随机生成了0到1000之间的数,来作为sleep的时间(以毫秒为单位)
public class ClientThread extends Thread{
private final Random random;
private final RequestQueue requestQueue;
public ClientThread(RequestQueue requestQueue,String name,long seed){
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run(){
for (int i = 0; i < 1000; 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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
ServerThread类用于表示接收请求的线程。该类持有RequestQueue的实例 (requestQueue).
ServerThread使用getRequest方法接收请求
public class ServerThread extends Thread{
private final Random random;
private final RequestQueue requestQueue;
public ServerThread(RequestQueue requestQueue,String name,long seed){
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run(){
for (int i = 0; i < 1000; i++) {
Request request = requestQueue.getRequest();
System.out.println(Thread.currentThread().getName()+"handles:"+request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Main类会首先创建RequestQueue的实例(requestQueue),然后分别创建名为Alice的实例ClientThread和名为Boddy的实例ServerThread,并将requestQueue传给这两个实例,最后执行start.
public class Main {
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
new ClientThread(requestQueue, "Alice", 3141592L).start();;
new ClientThread(requestQueue, "Bobby", 6535897L).start();;
}
}
GuardedObject 角色是一个持有被守护的方法的类.当线程执行guardedMethod方法时,如果条件成立,则可以立即执行,当守护条件不成立,则进行等待。
三大特征:
存在循环
存在条件检查
因为某种原因而等待
guarded supension :被守护而暂停执行的含义
guarded wait:被守护而等待
采用LinkedBlockingQueue时,实例程序中的RequestQueue可以被简化。
take方法用于取出队首的元素(将队头元素出队,如果队列空了,一直阻塞,直到队列不为空或者线程被中断),put方法用于向队列末尾添加元素(在队尾插入一个元素,如果队列满了,一直阻塞,直到队列不满了或者线程被中断)。这两个方法都进行了封装。因为这两个方法已经考虑了互斥的方法,所以无需声明为synchroized方法。
public class RequestQueue {
private final BlockingQueue<Request> queue = new LinkedBlockingQueue<Request>();
public Request getRequest(){
Request req = null;
try {
req = queue.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//取出队首元素
return req;
}
public void putRequest(Request request){
try {
queue.put(request);//向队列末尾添加元素
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
LinkedList和LinkedBlockingQueue的比较
对于Guarded Supension模式的总结:
该模式存在一个持有状态的对象,只有在这个对象的状态满足条件的情况下,才会允许现在执行目标处理。所以,我们先将这个对象的满足条件作为它的“守护条件”,然后,在执行这个目标处理之前,检查守护条件是否成立,只有当守护条件成立时,线程才会执行目标处理,而当守护条件不成立时,线程就会一直等到成立为止,使用while条件嵌套检查条件,wait()执行等待,使用notifyAll()方法进行通知。这就是Guarded Suspension模式。