Java

CompletableFuture を解らないまま試してみた

Java

Java8 の目玉機能は並行処理を容易にするために採用された Lambda に注目が集まってます。

その他にも便利な機能が追加されているのですが何故か人気の無い? CompletableFuture を試してみました。

これも外国語のサイトからの情報を元に解らないままゴニョゴニョしてみました。

ちなみに CompletableFuture の情報はグーグル先生に聞いてみても新しい情報は数件しかヒットしませんでした。

誰かが日本語で優しく解説してくれるのを待っていたのですが Lambda の人気の高さからか見向きもされない可愛そうな子となってます。(ヲヒ

CompletableFuture っていったい何者なの?

これまでは非同期処理の返り値を取るためには ExecutorService、 Callable、Future インターフェースを使っていました。

この CompletableFuture を使えばそれらを簡単にできちゃうよって優れもののようです。

それでは試してみましょう。

だいたいの流れをサクッと説明すると、

それぞれ、countHoge() , countFuga() , countChome() と言うメソッドで時間のかかる計算結果を取得するものとします。

それぞれ 1 秒、3 秒、7 秒と計算時間を要します。

返り値は、1 、3 、7 とします。

もちろん ExecutorService を利用します。

それぞれ CompletableFuture.supplyAsync() にて CompletableFuture を生成します。

CompletableFuture.allOf() メソッドにてそれぞれをひとまとめにします。

thenRun() メソッドにて 処理を開始し、計算結果を返します。

あとは get() メソッドにて計算結果を取得すればいいだけです。

返り値を利用して新たに CompletableFuture<T> を作ることも可能です。

この CompletableFuture クラスには 50 種類ほどのメソッドが用意されてるのでかなり自由度が高くいろんなことができそうです。

ちなみに良く解らないのに適当に創ったコードなので間違いがあると思いますが笑って許してくださいませ。

 

出力結果は次のようになります。

 

<—  START  —>
1
3
7
<—  DONE  —>
処理時間は、0時間0分6秒9916577070000008
monad = 3.141592653589793
executor.shutdown()

 

処理時間からそれぞれの処理が平行実行されていることが解りますね。(^_^)

プロファイラで確認してみました。

p1

thenRun() メソッドを使用すると上記のような結果となりますが

thenRunAsync() メソッドというものもあります。

これは  ForkJoinPool.commonPool() を使っているようです。

若干 thenRun() メソッドと動作が違います。

こちらはメソッド内の処理が ExecutorService に全て乗らない場合があります。

登録されたタスクが終了した時点で shutdown 開始するとshutdown 開始までに ExecutorService に乗っていれば当然処理されるのですがタイミングが悪いと間に合わず未処理となってしまいます。

37 行目から 60 行目の計算結果取得処理をコメントアウトしてそれぞれ実行してみると良く解ります。

thenRun() メソッドの場合は次のように全て実行され出力されます。

 

<—  START  —>
<—  DONE  —>
executor.shutdown()
処理時間は、0時間0分6秒9965874390000007

 

thenRunAsync() メソッドを使用した場合は次のように一部処理されずプログラムが終了する場合があります。

 

<—  START  —>
executor.shutdown()
<—  DONE  —>

 

このあたりは微妙に違うので注意が必要ですね。

ついでに、thenRunAsync() メソッドを使用した場合のプロファイラでみてみました。

ForkJoinPool.commonPool() が存在してますね。

p2

これらの使い分けのシチュエーションが思いつきません。(^_^;)

さて、時間のかかる処理をするのはいいけどあまりにも時間がかかりすぎた場合はデフォルト値を返すようにしたい場合がありますよね。

そんな時のために get(long timeout, TimeUnit unit) メソッドと complete(T value) メソッドが用意されてます。

プログラムに次のコードを追加して動かしてみましょう。

 

fuga.get(2, TimeUnit.SECONDS) 2 秒でタイムアウトして fuga.complete(99) で fuga に 99 を設定します。

もちろん例外もスローされます。

ではプログラムを実行して確認してみましょう。

 

<—  START  —>
1
99
7 08, 2013 12:13:28 午前 jp.yucchi.trycompletablefuture.TryCompletableFuture main
SEVERE: null
java.util.concurrent.TimeoutException
at java.util.concurrent.CompletableFuture.timedAwaitDone(CompletableFuture.java:376)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1555)
at jp.yucchi.trycompletablefuture.TryCompletableFuture.main(TryCompletableFuture.java:39)

<—  ERROR  —>
7
処理時間は、0時間0分6秒9896285360000006
monad = 3.141592653589793
executor.shutdown()

 

ちゃんとタイムアウトして例外を投げてそして fuga に 99 をセットしてますね。

fuga の値が 99 になることによって hoge, fuga, chome の合計値が 11 と等しくならないので <—  ERROR  —> と判定され出力されてます。

もう、ExecutorService、 Callable、Future インターフェース の時代はオワコンかな?

ざっと確認したけど間違ってる可能性が高いので日本語で誰かが詳しく解説してくれるのを待つとしましょうか。

かなり CompletableFuture っていけてる気がします!

Java8 早くリリースしておくれ!

cludia_2013_07_08_001

Hatena タグ:

もしかしたら代入に失敗するかもしれない

Java

今日 Twitter で見かけた素晴らしいコードに感動しました。


そこで私も Java8 で創ってみました。

実行結果は次のようになります。
ただ、本当に代入に失敗したかどうかは解りませんw

7

さすがにログをとって何回もとは思いませんでした。
誰かもっと安心できるコードをご存じでしたらご教示お願いいたします。

Hatena タグ:

もっと Lambda その18

Java

昨日のエントリーの解答を載せておきます。

10
8
0
4
2
6

上記のように 0 から 10 までの偶数が表示されます。

出力される順番はプログラムが実行される環境により変わります。

これは parallel() メソッドにより平行ストリームとして処理されるからです。

JavaDOC では下記のようになってます。

java.​util.​stream.​IntStream

public IntStream parallel()

Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.

This is an intermediate operation.

戻り値:

a parallel stream

それでは順序よく 0 から 10 までの偶数を表示させるにはどうすればいいでしょうか?

そんな我が儘なあなたに使って欲しいメソッドが用意されてます。

java.​util.​stream.​IntStream

public void forEachOrdered(IntConsumer action)

Performs an action for each element of this stream, guaranteeing that each element is processed in encounter order for streams that have a defined encounter order.

This is a terminal operation.

パラメータ:

action – a non-interfering action to perform on the elements

参照:

IntStream.forEach(IntConsumer)

 

これを使って昨日のプログラムを変更してみます。

ついでにメソッドリファレンスを使うように変更しました。

実行結果は期待通りに順序よく出力されます。

0
2
4
6
8
10

検証はしてないのですがコストが高くなるようなので使いどころを誤らないようにしなければいけないようです。

簡単なパズルでしたけどなんでもいいから平行ストリーム使っちゃえ!ってやってたらはまる可能性がありますね。(私のような単純で純粋なひとはw)

Hatena タグ:

もっと Lambda その17

Java

今日も Java8 ネタです。

すでに Lambda のメーリングリストでやりとりされていたネタを Java パズルとして出題します。

次のプログラムを実行するとどうなるでしょうか?

超ウルトラ簡単なパズルです。

答えは気が向いたら投稿するかもしれません。

あしからず・・・

Hatena タグ:

もっと Lambda その16

Java

Java Platform, Standard Edition 8 Early Access with Lambda Support b94 の optional クラスに filter map flatMap など愉快な仲間達が追加されたようです。

早速試してみました。

ネタ元はこちらです。

Java8でのプログラムの構造を変えるOptional、ただしモナドではない

この記事ではモナドについて触れられてますが私はモナドさっぱり解りません。

それって最中の親戚で甘くて美味しいの?ってレベルです。(^_^;)

それではまるっとコードを美味しくいただいて動くかどうか試してみます。

コードはいつものデータを利用します。

無駄に長いです。

<– Person  –>
柴田 恭平, 61歳, Gender: MALE   体重 : 70   評価 : 8
壇 蜜, 32歳, Gender: FEMALE   体重 : 60   評価 : 6
北川 景子, 26歳, Gender: FEMALE   体重 : 55   評価 : 7
綾瀬 はるか, 28歳, Gender: FEMALE   体重 : 50   評価 : 4
佐々木 希, 25歳, Gender: FEMALE   体重 : 48   評価 : 9
剛力 彩芽, 20歳, Gender: FEMALE   体重 : 45   評価 : 6
小栗 旬, 30歳, Gender: MALE   体重 : 65   評価 : 3
堀北 真希, 24歳, Gender: FEMALE   体重 : 45   評価 : 10
武井 咲, 19歳, Gender: FEMALE   体重 : 50   評価 : 5
市原 隼人, 26歳, Gender: MALE   体重 : 67   評価 : 2
深田 恭子, 30歳, Gender: FEMALE   体重 : 50   評価 : 8

上記のリストで一番若い男性のもとへ一番若い女性がお嫁入りするとしてその女性の新しい姓名を表示させてみます。

ほげほげ@ちょめちょめ ってね。

実行結果は以下のようになります。

市原

市原@咲

期待通りに動いてるけどこれが可能になることが Java な人には重要なことなんだろうか?

モナドを美味しくいただけくことができない私にはイマイチ理解ができないです。

もしこれが重要なことだとしたらそのうち誰かが記事にして熱く語ってくれるだろう。

Hatena タグ:

« 古い記事 新しい記事 »