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();
これでよりスレッドセーフになる。