ExecutorService

ExecutorServiceはスレッドを管理するクラス。

マルチスレッドで並行処理をしたい場合、無制限にスレッドがCPUを専有することを防ぐために、 キューなどを使って最大スレッド数を制限する必要がある。
ExecutorServiceを使うと、自分で実装しなくても最大スレッド数を制限した並行処理を簡単に実現できる。

ExecutorServiceを使って最大スレッド数を指定して並行実行する

ExecutorServiceを使って最大スレッド数を指定して並行実行するには、 以下のようにExecutors.newFixedThreadPoolを使ってスレッドプールを作成する。

// 固定数のスレッド数のスレッドプールを作成
ExecutorService executor = Executors.newFixedThreadPool(スレッド数);

// スレッドを登録し、実行する
executor.execute(() -> {
  // 処理
});

// スレッドの登録を終了する
executor.shutdown();

実行例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main( String[] args ) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 20; i++) {
            int j = i;
            executor.execute(() -> {
                // スレッド名を表示
                System.out.println("Thread name: " + Thread.currentThread().getName() + ", index: " + j);
                sleep(500);
            });
        }

        executor.shutdown();
    }

    public static void sleep(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) { }
    }
}

実行結果は以下の通りとなる。 スレッドはpool-1-thread-1,pool-1-thread-2,pool-1-thread-3の3つが使い回されている。

Thread name: pool-1-thread-2, index: 1
Thread name: pool-1-thread-1, index: 0
Thread name: pool-1-thread-3, index: 2
Thread name: pool-1-thread-3, index: 5
Thread name: pool-1-thread-1, index: 3
Thread name: pool-1-thread-2, index: 4
Thread name: pool-1-thread-3, index: 6
Thread name: pool-1-thread-1, index: 8
Thread name: pool-1-thread-2, index: 7
Thread name: pool-1-thread-1, index: 9
Thread name: pool-1-thread-3, index: 10
Thread name: pool-1-thread-2, index: 11
Thread name: pool-1-thread-1, index: 12
Thread name: pool-1-thread-3, index: 13
Thread name: pool-1-thread-2, index: 14
Thread name: pool-1-thread-1, index: 15
Thread name: pool-1-thread-3, index: 17
Thread name: pool-1-thread-2, index: 16
Thread name: pool-1-thread-1, index: 18
Thread name: pool-1-thread-3, index: 19

すべてのスレッドが終了するまで待つ

awaitTerminationを使うと、すべてのスレッドが終了するまでブロックする。

// 固定数のスレッド数のスレッドプールを作成
ExecutorService executor = Executors.newFixedThreadPool(スレッド数);

// スレッドを登録し、実行する
executor.execute(() -> {
  // 処理
});

// スレッドの登録を終了する
executor.shutdown();

// すべてのスレッドが終了するまでブロックする
executor.awaitTermination(タイムアウト, 時間単位);

実行例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main( String[] args ) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 20; i++) {
            int j = i;
            executor.execute(() -> {
                System.out.println("Thread name: " + Thread.currentThread().getName() + ", index: " + j);
                sleep(500);
            });
        }

        executor.shutdown();
        System.out.println("All tasks submitted.");

        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        System.out.println("done");
    }

    public static void sleep(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {

        }
    }
}

実行結果は以下の通りとなる。

All tasks submitted.
Thread name: pool-1-thread-3, index: 2
Thread name: pool-1-thread-1, index: 0
Thread name: pool-1-thread-2, index: 1
Thread name: pool-1-thread-2, index: 3
Thread name: pool-1-thread-1, index: 4
Thread name: pool-1-thread-3, index: 5
Thread name: pool-1-thread-1, index: 6
Thread name: pool-1-thread-3, index: 8
Thread name: pool-1-thread-2, index: 7
Thread name: pool-1-thread-2, index: 10
Thread name: pool-1-thread-3, index: 9
Thread name: pool-1-thread-1, index: 11
Thread name: pool-1-thread-2, index: 12
Thread name: pool-1-thread-1, index: 13
Thread name: pool-1-thread-3, index: 14
Thread name: pool-1-thread-2, index: 15
Thread name: pool-1-thread-1, index: 16
Thread name: pool-1-thread-3, index: 17
Thread name: pool-1-thread-2, index: 18
Thread name: pool-1-thread-1, index: 19
done

すべてのスレッドが終了するまでブロックするので、doneが最後に表示される。

awaitTerminationでタイムアウトした場合もsubmit済みのタスクは実行される

awaitTerminationで指定したタイムアウト時間を経過した場合でも、すでにsubmit済みのタスクは実行される。

以下の様にタイムアウト時間を1秒に設定して実行してみる。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main( String[] args ) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 20; i++) {
            int j = i;
            executor.execute(() -> {
                System.out.println("Thread name: " + Thread.currentThread().getName() + ", index: " + j);
                sleep(500);
            });
        }

        executor.shutdown();
        System.out.println("All tasks submitted.");
        // 1秒でタイムアウトする
        executor.awaitTermination(1, TimeUnit.SECONDS);
        System.out.println("done");
    }

    public static void sleep(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {

        }
    }
}

実行結果は以下の通りとなる。

All tasks submitted.
Thread name: pool-1-thread-1, index: 0
Thread name: pool-1-thread-2, index: 1
Thread name: pool-1-thread-3, index: 2
Thread name: pool-1-thread-3, index: 3
Thread name: pool-1-thread-1, index: 4
Thread name: pool-1-thread-2, index: 5
done
Thread name: pool-1-thread-2, index: 6
Thread name: pool-1-thread-3, index: 7
Thread name: pool-1-thread-1, index: 8
Thread name: pool-1-thread-3, index: 9
Thread name: pool-1-thread-1, index: 11
Thread name: pool-1-thread-2, index: 10
Thread name: pool-1-thread-3, index: 12
Thread name: pool-1-thread-1, index: 14
Thread name: pool-1-thread-2, index: 13
Thread name: pool-1-thread-3, index: 15
Thread name: pool-1-thread-2, index: 16
Thread name: pool-1-thread-1, index: 17
Thread name: pool-1-thread-3, index: 18
Thread name: pool-1-thread-2, index: 19

doneが表示された後も登録済みのタスクは実行されている。