Java EE 勉強会 (第38回)
Java 並行処理プログラミング
第8章と第9章。担当はせとあずささん。
興味をもった所だけメモ。
P.192 の List 8-1 がなぜデッドロックするか理解できない。
public class ThreadDeadLock { ExecutorService exec = Executors.newSingleThreadExecutor(); public class RenderPageTask implements Callable<String> { public String call() throws Exception { Future<String> header, footer; header = exec.submit(new LoadFileTask("header.html")); footer = exec.submit(new LoadFileTask("footer.html")); String page = renderBody(); // デッドロックする -- タスクはサブタスクの結果を待ち続ける return header.get() + page + footer.get(); } } }
RenderPageTask が Callable を implements していることから想像すると、どこかから次のように呼び出されているためか?
Future<String> page = exec.submit(new RenderPageTask());
そうだとすると、やっぱりこの1行がサンプルにほしい。header、footer と Future が2つあるのも、わかりにくさに拍車をかけているから、こちらはどちらか1つにして。
P.193 で、
計算集約的なタスクでは、プロセッサ数が N のシステムは N+1 スレッドのスレッドプールを最も効率的に利用できます (計算集約的なスレッドでもページフォルトなどで休止することがあるので、そんなときの CPU のアイドルサイクルを防ぐために、1だけ多くのスレッドを用意します)。
1回読んだだけだと、+1 する理由がよくわからなかったけれど、わかってしまうと、( ) 内にちゃんと理由が書かれていますね。(^^;)
次のような定義があると:
N = CPU の数
U = CPU 稼働率 (0 < U < 1)
W/C = ウェイト時間と計算時間の比率
T = プールのスレッド数
プロセッサを目的の稼動率 U に維持するための最適プールサイズは、こうなります:
T = N * U * (1 + W/C)
具体例で考えてみます。CPU の数 N = 4 とします。稼働率75%とすると、実質的に使える CPU の論理的な数は 4 * 0.75 = 3 となります。ある処理を行うとき、計算時間 C に対して、ウェイト時間が 2C あるとします。そうすると、CPU を効率的に使用するためには、下の図のように CPU の数の (1 + 2) 倍のスレッドを同時に起動すると良いことがわかります。
C W |-----|-----------| C W |-----|-----|-----| C W |-----|-----|-----|
稼働率100%、すなわち U = 1 の場合、上記の式は、
T = N * (1 + W/C) = N + N * W/C
となります。ここで、純粋な計算処理のようにウェイト時間が極めて短い場合、W/C は限りなく0に近づきますが、でも0にはなりません。これを整数に切り上げると、最初の N + 1 になるというわけですね。
ここまでのところでつまづいていたおかげで、第8章の後半はほとんど聴いていませんでした。(^^;)
P.225 で、Void クラスが出てくるが、存在は知っていたものの初めて実際に使っているコードを見た。
長時間の GUI タスクで、別スレッドで処理を実行して値を戻すとき、スレッドセーフかどうかに注意!! 従来の方法では SwingUtilities.invokeLater を使用する。Java 5 なら Future を使うと良い。Java 6 なら SwingWorker?