#Guarded Suspensionパターン
Guarded Suspensionパターンにはguarded wait, spin lockなどの呼び名がある。
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 + " ]";
}
}
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(); // 待っているスレッドに、キューにrequestが入ったことを知らせる
}
}
import java.lang.Math;
public class ClientThread extends Thread {
private final RequestQueue requestQueue;
public ClientThread(RequestQueue requestQueue, String name) {
super(name);
this.requestQueue = requestQueue;
}
public void run() {
for (int i = 0; i <= 100; i++) {
Request request = new Request("No." + i);
System.out.println(Thread.currentThread().getName() + " put " + request);
requestQueue.putRequest(request);
try {
Thread.sleep((int)(Math.random()*10));
} catch (InterruptedException e) {
}
}
}
}
import java.lang.Math;
public class ServerThread extends Thread {
private final RequestQueue requestQueue;
public ServerThread(RequestQueue requestQueue, String name) {
super(name);
this.requestQueue = requestQueue;
}
public void run() {
for (int i = 0; i <= 100; i++) {
Request request = requestQueue.getRequest();
System.out.println(Thread.currentThread().getName() + " get " + request);
try {
Thread.sleep((int)(Math.random()*10));
} catch (InterruptedException e) {
}
}
}
}
public class Main {
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
new ClientThread(requestQueue, "A").start();
new ServerThread(requestQueue, "B").start();
}
}
...
A put [ Request No.95 ]
B get [ Request No.91 ]
B get [ Request No.92 ]
A put [ Request No.96 ]
A put [ Request No.97 ]
B get [ Request No.93 ]
A put [ Request No.98 ]
B get [ Request No.94 ]
A put [ Request No.99 ]
B get [ Request No.95 ]
B get [ Request No.96 ]
B get [ Request No.97 ]
B get [ Request No.98 ]
B get [ Request No.99 ]
...
ClientThreadがSeverThreadにRequestのインスタンスを受け渡す。これは非常に簡単なスレッド間通信。ClientThreadやServerThreadのことを能動オブジェクト、RequestQueueのことを受動オブジェクトと呼ぶ。
ClientThreadがQueueにリクエストを投げ(put)、ServerThreadがQueueからリクエストを取り出す(get)。putとgetはsynchronizedで排他されている。
getRequesメソッドは最も古いリクエストを一つ取り出す。リクエストが一つもない場合は、他のスレッドがputRequestするまで待つ。queue.peek() != nullという条件つまりQueueが空でないという条件が満たされている必要がある。このような、満たされていなければならない条件のことをGuarded Suspensionパターンのガード条件と呼ぶ。
putRequestメソッドは、リクエストを一つ追加する。その後notifyAllで待っているスレッドに知らせる。
waitとnotifyについては
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その1)
を参照。
実行結果の通り、B(get)がA(put)を追い越すことはない。
##登場人物
GuardedObject役は、ガードされたメソッド(guardedMethod)を持っているクラス。ガード条件が満たされていればすぐに実行され、満たされていなければ待たされる。GuardedObjec役は、インスタンスの状態を変化させるメソッド(stateChangingMethod)を持つ場合がある。サンプルプログラムではRequestQueueクラスがこの役をつとめ、getRequestがguardedMethod、putRequestがstateChangingMethodに対応する。
関連
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その1)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その2)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その3)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その4)