6
4

More than 3 years have passed since last update.

JavaでのスレッドとCallableの基本【初心者】

Last updated at Posted at 2021-01-02

JavaでのThreadの基本

マルチスレッドを使えば複数処理を並列に走らせたりできる。
こういう処理の一つ一つをスレッドと呼ぶ。
Javaにおいてスレッド処理をするには継承(extends)を使う方法とインターフェイスを実装(implements)して使う方法がある。
前者の場合はThreadクラスを継承してstart()メソッドを呼び出す。
後者の場合はRunnableインターフェイスを実装してrun()メソッドを呼び出す。
Javaでは多重継承ができないため、Threadを使う方法だと他のクラスを継承できないが、インターフェイスを使った方法であれば可能。
後者の場合はCallableインターフェイスを実装してcall()メソッドを呼び出す方法もある。

RunnableとCallableの違い

Runnableの場合は返り値の指定ができず、例外も飛ばせない。

Runnable_interface
public interface Runnable {
    public abstract void run();
}

こんな感じで実装する。run()メソッドをoverrideする。

Runnable_implements
static class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnableだよ");
    }
}

Callableの場合は返り値の指定ができ、例外を飛ばせる。

Callable_interface
public interface Callable<V> {
    V call() throws Exception;
}

こんな感じで実装する。call()メソッドをoverrideする。
<>の部分がジェネリクスと呼ばれる部分で、返り値の型を指定できる。(以下ではString)

Callable_implements
static class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        String result = "Callableだよ";
        return result;
    }
}

Callableの呼び出し

オブジェクトを作ってCallableを使ってみたい。

main
MyCallable myCallable = new MyCallable();

ここでFutureExecutorServiceを使うと便利。

main
// ExecutorServiceインスタンスを生成
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Executorにスレッドの実行を依頼
Future<String> future = executorService.submit(myCallable); 
//結果を取り出すにはget()メソッドを使う
String result = future.get(100, TimeUnit.MILLISECONDS);

ExecutorService経由でmyCallableを実行し、結果をFuture型の変数に格納している。
返り値の型はジェネリクスで指定。上ではStringにしている。
結果はFutureget()メソッドを使って取り出す。
また、上のように引数を指定することで、0.1秒以上かかった場合に例外を発生させたりできる。

よくわかってない僕からすると、
「Runnableの時みたいにmyCallable.call()を直接呼び出すのじゃだめなの?」
とか思ったりしたのだけど、
myCallableの処理は別スレッドで行われるため、処理が終わってから返り値が返ってくるのを待ってなきゃいけないのよね。
ExucutorServiceを使えばちゃんと処理が終わるまで待った上で、返り値をfutureオブジェクト経由で取得可能ということだと思う。(多分)
Futureっていう名前も未来っぽいし。

ThreadLocalを使った方法

classのstaticメンバは共有リソースとして管理されるために、別のスレッドから書き換え可能になってしまう。
スレット内で完結させるにはThreadLocalを使うと便利。

main
// ThreadLocalのインスタンスを作成
private final ThreadLocal<ExecutorService> executorServicePool = new ThreadLocal<>();
// SingleThreadExecutorをセットする
executorServicePool.set(Executors.newSingleThreadExecutor());
//結果を取り出すにはget()メソッドを使う
ExecutorService executorService = executorServicePool.get();

これでよりスレッドセーフになる。

参考にしたページ

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4