JavaでのThreadの基本
マルチスレッドを使えば複数処理を並列に走らせたりできる。
こういう処理の一つ一つをスレッドと呼ぶ。
Javaにおいてスレッド処理をするには継承(extends)を使う方法とインターフェイスを実装(implements)して使う方法がある。
前者の場合はThreadクラスを継承してstart()メソッドを呼び出す。
後者の場合はRunnableインターフェイスを実装してrun()メソッドを呼び出す。
Javaでは多重継承ができないため、Threadを使う方法だと他のクラスを継承できないが、インターフェイスを使った方法であれば可能。
後者の場合はCallableインターフェイスを実装してcall()メソッドを呼び出す方法もある。
RunnableとCallableの違い
Runnableの場合は返り値の指定ができず、例外も飛ばせない。
public interface Runnable {
public abstract void run();
}
こんな感じで実装する。run()メソッドをoverrideする。
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnableだよ");
}
}
Callableの場合は返り値の指定ができ、例外を飛ばせる。
public interface Callable<V> {
V call() throws Exception;
}
こんな感じで実装する。call()メソッドをoverrideする。
<>の部分がジェネリクスと呼ばれる部分で、返り値の型を指定できる。(以下ではString)
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String result = "Callableだよ";
return result;
}
}
Callableの呼び出し
オブジェクトを作ってCallableを使ってみたい。
MyCallable myCallable = new MyCallable();
ここでFutureとExecutorServiceを使うと便利。
// ExecutorServiceインスタンスを生成
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Executorにスレッドの実行を依頼
Future<String> future = executorService.submit(myCallable);
//結果を取り出すにはget()メソッドを使う
String result = future.get(100, TimeUnit.MILLISECONDS);
ExecutorService経由でmyCallableを実行し、結果をFuture型の変数に格納している。
返り値の型はジェネリクスで指定。上ではStringにしている。
結果はFutureのget()メソッドを使って取り出す。
また、上のように引数を指定することで、0.1秒以上かかった場合に例外を発生させたりできる。
よくわかってない僕からすると、
「Runnableの時みたいにmyCallable.call()を直接呼び出すのじゃだめなの?」
とか思ったりしたのだけど、
myCallableの処理は別スレッドで行われるため、処理が終わってから返り値が返ってくるのを待ってなきゃいけないのよね。
ExucutorServiceを使えばちゃんと処理が終わるまで待った上で、返り値をfutureオブジェクト経由で取得可能ということだと思う。(多分)
Futureっていう名前も未来っぽいし。
ThreadLocalを使った方法
classのstaticメンバは共有リソースとして管理されるために、別のスレッドから書き換え可能になってしまう。
スレット内で完結させるにはThreadLocalを使うと便利。
// ThreadLocalのインスタンスを作成
private final ThreadLocal<ExecutorService> executorServicePool = new ThreadLocal<>();
// SingleThreadExecutorをセットする
executorServicePool.set(Executors.newSingleThreadExecutor());
//結果を取り出すにはget()メソッドを使う
ExecutorService executorService = executorServicePool.get();
これでよりスレッドセーフになる。