【マルチスレッド】CompletableFutureについて

マルチスレッド処理で他のスレッドから戻り値がある場合がある。

戻り値を受け取る方法としてCompletableFutureがある。

マルチスレッドの中では、1つのスレッドの戻り値を受け取って処理を行いたい場合がある。

その場合、戻り値が必要な処理は、戻り値を受け取るまで処理がブロックされる。
戻り値を返す処理が終了したら、戻り値を取得し、後続の処理を行う。

CompletableFutureは前段の処理が終了したら自動的にその結果を次の処理を渡して実行するようにできる。

CompletableFutureは戻り値のある非同期処理をsupplyAsyncメソッドで起動する。


``` sample.java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Sample {

	public static void main(String[] args) throws Exception {

		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "str");

		String resultStr = future.get();
		System.out.println(resultStr);
	}
}
```

出力結果
```
str
```

getメソッドを使用すると戻り値を取得することができる。


しかし、getメソッドでは非同期処理が完了するまで、値を受け取ることができない。

それまではブロック状態になる。

ブロック状態を防ぐためには。非同期処理が終了後にそのスレッドで戻り値を使用し。次の処理を実行するようにする。

それを可能にするのがthenAcceptメソッドである。

``` sample.java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Sample {

	public static void main(String[] args) throws Exception {

		CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "str")
				.thenAccept(result -> {System.out.println("取得した戻り値は" + result);});

	}
}
```

出力結果
```
取得した戻り値はstr

```


thenAcceptによって値を返さなくなるので<Void>となる。

resultはsupplyAsyncから渡されたstrで後続の処理でstrを使用することができる。

このような後続処理用のメソッドは3つある。


| 1 | 2 |
|:-:|:-:|
|  thenAccept | 戻り値を受け取り、値を返さない処理を実行する  |
| thenApply  |  戻り値を受け取り、別の値を返す処理を実行する |
| thenRun  |  値を受け取らず、値を返さない処理を実行する |


# 非同期処理のエラー対策

非同期処理でエラーが発生したときにタイムアウトでスレッドを終了したり、例外対策をする仕組みがある。


``` sample.js
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Sample {

	public static void main(String[] args) throws Exception {

		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "str")
				.orTimeout(2, TimeUnit.SECONDS)
				.whenComplete((result, error) -> {
					if(error == null) {
						System.out.println("取得した戻り値は" + result);
					} else {
						System.out.println("エラーが発生");
					}
				});

	}
}
```

orTimeoutで指定時間の2秒が過ぎたときに非同期処理を終了させることができる。

whenComplete終了時の処理を指定することができる。

上記の場合、errorがnullだったら取得した戻り値を出力して、nullでなかったらエラー処理を行う。


# 非同期処理の結合


2つの非同期処理の終了を待ち合わせて両方の戻り値を使用して処理を行いたい場合はthenCombineメソッドを使用する。

``` sample.java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Sample {

	public static void main(String[] args) throws Exception {

		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
				.thenCombine(CompletableFuture.supplyAsync(() -> "World"), (result1, result2) -> result1 + result2)
				.whenComplete((result, error) -> {
					if(error == null) {
						System.out.println("取得した戻り値は" + result);
					} else {
						System.out.println("エラーが発生");
					}
				});

	}
}
```

出力結果
```
取得した戻り値はHelloWorld

```

1つ目のHelloを返す非同期処理があり、それをthenCombineを連結している。

thenCombineの第一引数は2つ目の非同期処理で第2引数は1つ目と2つ目の非同期処理の戻り値を使用した処理である。

結果的にwhenComplete内で使用しているresultは1つ目と2つ目の非同期処理の戻り値を使用した処理の戻り値である。