ExecutorServiceを利用したバックグラウンド処理

      2017/02/09

ExecutorServiceは複数のスレッドを一括で管理してくれる便利な機能です。

生成

インスタンスの生成には複数の方法があります。

  • submit()されたスレッドを並列に処理する。
    ExecutorService mExecutor = Executors.newCachedThreadPool();
  • submit()されたスレッドを順番に実行する。
    ExecutorService mExecutor = Executors.newSingleThreadExecutor();
  • submit()されたスレッドを並列に処理するが、最大同時実行スレッド数を指定できる。下記の場合は最大同時スレッド数2。
    ExecutorService mExecutor = Executors.newFixedThreadPool(2)

スレッドの実行(タスクの送信)

下記で実行します。スレッドに空きが出来たら実行(空きがあれば即実行)します。
mExecutor.submit({Runnable実装クラスのインスタンス});

Thread は Runnable を implements している。

終了(シャットダウン)

下記で順序正しくシャットダウンを開始します。送信済みのタスクは実行されます。
executor.shutdown();

基本的な使い方

バックグラウンド処理クラス

class BackgroundTask implements Runnable {
    @Override
    public void run() {
        // 処理
    }
}

呼び出し側

/* バックグラウンド処理の終了を待たない. */
void start() {
    ExecutorService executor = Executors.newCachedThreadPool();
    executor.submit(new BackgroundTask());
    executor.shutdown();
}

/* バックグラウンド処理の終了まで待機する. */
void startWithWaitBackgroundTask() {
    ExecutorService executor = Executors.newCachedThreadPool();
    Future future = executor.submit(new BackgroundTask());
    executor.shutdown();
    try {
        future.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

応用

メインスレッドからバックグラウンドスレッドへ順次データを渡して処理させる方法です。

バックグラウンドスレッドに渡すデータキューの部分

sModelExitは終了判定オブジェクト。

class Model {
    public byte[] data;
}

private LinkedBlockingQueue mQueue = new LinkedBlockingQueue<>();
private static final Model sModelExit = new Model();

Model の解放が必要な場合

private void initQueue() {
    while (0 < mQueue.size()) {
        Model model = mQueue.poll();
        if (model != null) {
            // modelのリリース
        }
    }
}

private void addQueue(Model model) {
    mQueue.offer(model);

    /* キューの最大数を指定する場合は以下を記述.
     * このサンプルでは後から追加されたデータを優先して残す.
     * MAX_NUM = キューの最大数
     */
    while (MAX_NUM < mQueue.size()) {
        Model model = mQueue.poll();
        if (model != null) {
            // modelのリリース
        }
    }
}

private void setExit() {
    addQueue(sModelExit);
}

Model の解放が不要な場合

private void initQueue() {
    mQueue.clear();
}

private void addQueue(Model model) {
    mQueue.offer(model);

    /* キューの最大数を指定する場合は以下を記述.
     * このサンプルでは後から追加されたデータを優先して残す.
     * MAX_NUM = キューの最大数
     */
    while (MAX_NUM < mQueue.size()) {
        Model model = mQueue.poll();
    }
}

private void setExit() {
    addQueue(sModelExit);
}

バックグラウンド処理クラス

class BackgroundTask implements Runnable {
    @Override
    public void run() {
        try {
            Model model = null;
            // take()は次が来るまで待つ.
            while ((model = mQueue.take()) != sModelExit) {
                // model を 処理.
                // 必要なら modelのリリース.
            }
        } catch (InterruptedException e) {
            // 待機中に割り込みが発生.
            e.printStackTrace();
        }
    }
}

呼び出し側

private final ExecutorService mExecutor = Executors.newCachedThreadPool();

void start() {
    mExecutor.submit(new BackgroundTask());
}

void onReceiveData(Model model) {
    addQueue(model);
}

public void onDestroy() {
    mExecutor.shutdown();
    // 最大100msec待って終了.
    try {
        boolean isExit = mExecutor.awaitTermination(100, TimeUnit.MILLISECONDS);
        if (isExit) {
            // 正常終了
        } else {
            // タイムアウト
        }
    } catch (InterruptedException iExp) {
        // 待機中に割り込みが発生
        mExecutor.shutdownNow();
        Thread.currentThread().interrupt();
    }
}

 - Java