Guarded Suspension 模式
Guarded是被保護(hù)、被守護(hù)的意思,Suspension是暫停的意思寿弱。就是說線程執(zhí)行當(dāng)前的處理會有問題笨篷,需要讓線程處理等待一下才睹。
實例程序
程序會用一個線程把請求的實例傳給另一個線程亮垫,相對簡單的線程間通信杆兵。
Request類
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 + " ]";
}
}
RequestQueue 類
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();
}
}
ClientThread類
用于發(fā)送請求的線程揪阿。ClientThread持有RequestQueue的實例疗我,并調(diào)用此實例放入請求。
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) {}
}
}
}
ServerThread 類
用于接受請求的線程南捂,持有RequestQueue的實例吴裤,使用getRequest方法接受請求。
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){}
}
}
}
Main類
Main類創(chuàng)建RequestQueue的實例溺健,然后分別創(chuàng)建多個ClientThread類的實例麦牺,并讓ClientThread的所有實例從RequestQueue存入和獲取請求,然后運(yùn)行線程鞭缭。
public class Main {
public static void main(String[] args){
RequestQueue requestQueue = new RequestQueue();
new ClientThread(requestQueue, "Alice", 31415926L).start();
new ClientThread(requestQueue, "Bobby", 6535897L).start();
}
}
java.util.Queue與java.util.LinkedList的操作
- Request remove()
從隊列移除第一個元素剖膳,并返回該元素,如隊列無元素岭辣,則拋出java.util.NoSuchElementException異常吱晒。 - boolean offer(Request req)
將元素req添加到隊列末尾 - Request peek()
如果隊列存在元素,則該方法會返回頭元素易结;如果為空枕荞,則返回null柜候。該方法并不移除元素。
java.util.LinkedList類是非線程安全的躏精。
java.util.concurrent.LinkedBlockingQueue是線程安全渣刷。
getRequest 詳解
getRequest 方法
public synchronized Request getRequest(){
while(queue.peek() == null){
try{
wait();
} catch (InterruptedException e){}
}
return queue.remove();
}
- 守護(hù)條件進(jìn)行保護(hù)
getRequest方法應(yīng)該執(zhí)行的“目標(biāo)處理”是什么。該方法的目的是“從queue中取出一個Request實例”矗烛,也就是執(zhí)行下面的這條語句
queue.remove();
但是辅柴,為了安全執(zhí)行這條語句,必須滿足如下條件瞭吃。
queue.peek() != null
該條件就是“存在想要取出的元素”碌嘀。這種必須要滿足的條件就稱為Guarded Suspension模式的守護(hù)條件(guard condition)。
queue.peek() != null ----守護(hù)條件
當(dāng)守護(hù)條件的邏輯不成立時歪架,絕對不會執(zhí)行后面的語句(調(diào)用remove)股冗。 - 不等待的情況和等待的情況
當(dāng)線程執(zhí)行while語句時,需要考慮守護(hù)條件成立與不成立這兩種情況和蚪。 - 執(zhí)行 wait止状, 等待條件發(fā)生變化
當(dāng)守護(hù)條件不成立時,線程執(zhí)行wait攒霹,開始等待怯疤,那到底在等待什么呢?
“是在等待notify/notifyAll催束〖停” 正在wait的線程如果不被notify/notifyAll,便會一直在等待隊列中抠刺。不過塔淤,我們需要思考一下更深層的含義。線程真正等待的是實例狀態(tài)的變化矫付。線程之所以等待凯沪,是因為守護(hù)條件未滿足,也就是該守護(hù)條件進(jìn)行了保護(hù)买优,從而阻止了線程繼續(xù)向前執(zhí)行妨马。線程等待的實例狀態(tài)發(fā)生變化,守護(hù)條件成立的時候杀赢。 - 執(zhí)行到while 的下一條語句時一定能確定的事情
getRequest方法烘跺,如果while的下一條語句肯定會執(zhí)行,那么執(zhí)行時脂崔, while語句的守護(hù)條件一定是成立的滤淳,也就是說,調(diào)用remove方法時砌左,“queue中存在可供取出的元素”脖咐,所以remove方法絕對不會拋出NoSuchElementException異常铺敌。
putRequest 詳解
public synchronized void putRequest(Request request){
queue.offer(request);
notifyAll();
}
執(zhí)行offer方法,向queue的末尾添加一個請求(request)屁擅。這時偿凭,queue中至少存在一個可供取出的元素。因此派歌,下面的表達(dá)式為真弯囊。
queue.peek() != null
在getRequest中,正在wait的線程等待的是什么呢胶果?就是上面這個條件匾嘱,即守護(hù)條件成立。那么早抠,這里就執(zhí)行notifyAll喚醒等待的線程霎烙。
synchronized的含義
getRequest和putRequest方法中都使用了synchronized。synchronized保護(hù)的是queue字段(LinkedList的實例)贝或。例如getRequest方法中的處理必須確保同時“只能有一個線程執(zhí)行”吼过。這就是Single Threaded Execution模式。
wait與鎖
線程執(zhí)行某個實例的wait方法咪奖。這時,線程必須獲取實例的鎖酱床。上面的synchronized方法中羊赵,wait方法被調(diào)用,獲取的就是this的鎖扇谣。
線程執(zhí)行this的wait方法后昧捷,就進(jìn)入this的等待隊列,并釋放持有的this鎖罐寨。
notify靡挥、notifyAll或interrupt會讓線程退出等待隊列,但在實際地繼續(xù)執(zhí)行處理之前鸯绿,還必須再獲取this鎖跋破。
Guarded Suspension模式中的登場角色
GuardedObject(被守護(hù)的對象)
GuardedObject角色是一個持有被守護(hù)的方法(guardedMethod)的類。當(dāng)線程執(zhí)行g(shù)uardedMethod方法時瓶蝴,若守護(hù)條件成立毒返,則可以立即執(zhí)行;當(dāng)守護(hù)條件不成立時舷手,就要進(jìn)行等待拧簸。守護(hù)條件的成立與否會隨著GuardedObject角色的狀態(tài)不同而發(fā)生變化。
除了guardedMethod之外男窟,GuardedObject角色還有可能持有其他改變實例狀態(tài)(特別是改變守護(hù)條件)的方法(stateChangingMethod)盆赤。
在Java中贾富,guardedMethod通過while語句和wait方法來實現(xiàn)。stateChangingMethod則通過notify/notifyAll方法來實現(xiàn)牺六。
在示例程序中祷安,由RquestQueue角色類扮演此類。getRequest方法隊形guardedMethod兔乞,putRequest方法則對應(yīng)stateChangingMethod汇鞭。