ここでは、これらのうちで、FutureクラスとCompletableFutureクラスを取り上げます。下記のJava書籍[1]の第12章には、「マルチスレッドと平行処理」が詳しく解説されています。その中にある例題を参考にして検討します。例えば、以下の図1ような図を描くとします。まず、5角形と8角形をそれぞれ別のスレッドで並行してして描きます。それら両方の描画が完了するのを待って、次に外側に4角形を描きます。この四角形の一辺の長さは、図に示したように、5角形と8角形を描き終わった後に決まるとしています。(短いビデオもご覧ください。)
これらの多角形を描くメソッドpolyDとpolyVを以下のように定義します。
double polyD(int x, int y, int n, int s)
void polyV(int x, int y, int n, int s)
x, yは多角形描画の開始点の座標
nは頂点の数
sは一辺の長さ
polyDは、n * sの値を返します。
まず、ExecutorとFutureを使ったプログラム(リスト1)を以下に示します。ExecutorService(スレッドプール)を使って、2つの多角形の描画を別々のスレッドへsubmitします。(line 21-23)すると、Futureオプジェクトx1, x2が直ちに返ってきます。したがって、描画を待たずに、両方の多角形の周の長さの和を求める計算(line 25)の開始に向かいます。しかし、ここで、x1.get()とx2.get()が呼び出されていることに注意しましょう。すなわち、このget()は、それぞれの多角形の描画の完了を待つことを意味します。それが終わった後に、外側の四角形が描かれます。(line 26)
次に、文献[2]などを参考にして、CompletableFutureを使ったプログラム(リスト2)を以下に示します。これは、上記のExecutorとFutureを使ったプログラムと完全に同じ動作をします。しかし、注目すべきは、これが1ステートメントで完結していることです。Threadプールの利用も、2つの多角形描画の待ちも明示されていません。5角形と8角形の描画のための2つのCompletableFutureが、.thenCombineAsynによって連結され、さらにその後に起こる、四角形の描画も.thenAcceptで連結されています。並行処理部分があるのに、すべてが一気にパイプライン連結されています。なお、line 34は、両方の多角形描画の後に得られる、周の長さの和を計算するラムダ式です。
|
リスト1も十分明快ではありますが、このリスト2の方が、さらに洗練されたエレガントなプログラムであると言えるのではないでしょうか!別の言い方をすると、リスト1は、「ここで待て。そこで計算して、その結果を使って次の描画をせよ。」という命令的なのに比べて、リスト2は「2つの並行処理が終ったらその結果を使って次の描画を行う。」という仕様的記述だとも言えます。もちろん、状況により、使い分けることになるでしょう。
リスト1とリスト2の両方に言えることですが、Java8で導入されたLambda Expressionsが重要な役割を果たしていることも再認識できると思います。
さらに複雑な図2のような並列描画プログラムも、このCompletableFutureを使って書いてみます。これは、図1の描画を2つ横に並べて同時に描かせます。すなわち、さらに多重の並列プログラムになっています。
具体的なやり方は色々あるでしょうが、ここではリスト3のように、図1相当のものを2つ、.thenCombineAsyncで連結しました。なお、図1の描画に相当するプログラムは、メソッドCPF(ss)として設定しました。ここで、引数ssは、各図の描画を開始位置を決めるためのシフト量です。(なお、line 43のラムダ式(x,y)->y-xは、念のため、外側の図の開始位置の差を確認し、その値をline 44で表示していますが、特別な意味はありません。)
|
|
[1]立木秀樹、有賀妙子著「すべての人のためのJavaプログラミング 第3版」、共立出版
[2]C.S. Horstmann(柴田芳樹訳)「Java SE 8 実戦プログラミング」、インプレス
[2]C.S. Horstmann(柴田芳樹訳)「Java SE 8 実戦プログラミング」、インプレス
0 件のコメント:
コメントを投稿