プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

スレッドデザインパターン - Gurded Suspentation

Conditionを使って3章の「Gurded Suspentation」を書いてみた。
このサンプルは、ClientThreadがRequestを出して、ServerThreadがRequestを非同期に処理する、というもの。Requestの一時置き場にRequestQueueを使っている。
ちなみにこのRequestQueueは、実際にはBlockingQueueを使うべき(しかし、これを使うと例がなくなるので、あえて使わなかった)。
ClientThread, ServerThreadが複数になるバージョンは5章の「Producer-Consumer」になる。
また、ClientThread, ServerThreadが同期してRequestを処理するときは、Exchangerを使う。
Main.java

package dp.chap03;

public class Main {
	public static void main(String[] args)
	throws Exception {
		RequestQueue requestQueue = new RequestQueue();
		
		new ClientThread(requestQueue, "Alice").start();
		new ServerThread(requestQueue, "Bobby").start();
	}
}

Request.java

package dp.chap03;

public class Request {
	private final String name_;
	
	public Request(String name) {
		name_ = name;
	}
	
	public String getName() {
		return name_;
	}
	
	public String toString() {
		return "[Request " + getName() + " ]";
	}
}

ClientThread.java

package dp.chap03;

import java.util.*;

public class ClientThread extends Thread {
	private Random 		random_ = new Random();
	private RequestQueue 	requestQueue_;
	
	public ClientThread(RequestQueue requestQueue, String name) {
		super(name);
		requestQueue_ = requestQueue;
	}
	
	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 { sleep(random_.nextInt(1000)); }
			catch(InterruptedException ie) {}
		}
	}
}

ServerThread.java

package dp.chap03;

import java.util.*;

public class ServerThread extends Thread {
	private Random 			random_ = new Random();
	private RequestQueue	requestQueue_;
	
	public ServerThread(RequestQueue requestQueue, String name) {
		super(name);
		requestQueue_ = requestQueue;
	}
	
	public void run() {
		for(int i=0; i<10000; ++i) {
			try {
				Request request = requestQueue_.getRequest();
				System.out.println(Thread.currentThread().getName() + " handles " + request);
			
				sleep(random_.nextInt(1000));
			}catch(InterruptedException ie) {
				return;
			}
		}
	}
}

RequestQueue.java

package dp.chap03;

import java.util.*;
import java.util.concurrent.locks.*;

public class RequestQueue {
	private final LinkedList<Request> 	queue_ 		= new LinkedList<Request>();
	private Lock						mutex_ 		= new ReentrantLock();
	private Condition					notEmpty_ 	= mutex_.newCondition();
	
	public Request getRequest()
	throws InterruptedException {
		mutex_.lock();
		
		try {
			while(queue_.isEmpty()) {
				notEmpty_.await();
			}
			
			return queue_.removeFirst();
		}finally {
			mutex_.unlock();
		}
	}
	
	public void putRequest(Request request) {
		mutex_.lock();
		
		try {
			queue_.addLast(request);
			notEmpty_.signalAll();
		}finally {
			mutex_.unlock();
		}
	}
}

実行結果がこれ↓

Alice requests [Request No.0 ]
Bobby handles [Request No.0 ]
Alice requests [Request No.1 ]
Bobby handles [Request No.1 ]
Alice requests [Request No.2 ]
Alice requests [Request No.3 ]
Alice requests [Request No.4 ]
Bobby handles [Request No.2 ]
Bobby handles [Request No.3 ]
Alice requests [Request No.5 ]
Alice requests [Request No.6 ]
Bobby handles [Request No.4 ]
Bobby handles [Request No.5 ]
Alice requests [Request No.7 ]
Alice requests [Request No.8 ]
Alice requests [Request No.9 ]
Bobby handles [Request No.6 ]
Alice requests [Request No.10 ]
Bobby handles [Request No.7 ]
Bobby handles [Request No.8 ]
Bobby handles [Request No.9 ]
Alice requests [Request No.11 ]
Alice requests [Request No.12 ]
Bobby handles [Request No.10 ]
Bobby handles [Request No.11 ]
Alice requests [Request No.13 ]
Bobby handles [Request No.12 ]