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

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

ExecutorServiceの生成

インスタンスの生成には複数の方法があります。インスタンスの生成方法でスレッドの実行方法を指定します。

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

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

下記で実行します。
スレッドに空きが出来たら実行します。スレッドに空きがある場合は即実行します。

mExecutor.submit({Runnable実装クラスのインスタンス});
Thread は Runnable を implements している。

ExecutorServiceの終了(シャットダウン)

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

executor.shutdown();

バックグラウンド処理の終了まで待機させる

submit()の戻り値に対して、future.get()することで、そのスレッドが終了するまで待機します。下記は実行中の全スレッドが終るまで待つ場合。

// 実行スレッドを管理するリスト.
List<Future<?>> mFutureList = new ArrayList<>();
// 実行スレッドの保持.
Future<?> future = mExecutor.submit({Runnable});
mFutureList.add(future);

// 終了処理
mExecutor.shutdown();
for (Future<?> future : mFutureList) {
    try {
        future.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}
mFutureList.clear();

バックグラウンド処理の終了まで一定時間待機させる

ExecutorServiceのメソッドです。shutdown()実行後にコールします。

boolean awaitTermination(long timeout, TimeUnit unit)

基本的な使い方

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

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();
    }
}

使い方の応用

メインスレッドからバックグラウンドスレッドへ順次データを渡して処理させる方法です。
データの受け取りにはLinkedBlockingQueueを使います。

  • offer()キューの末尾に要素を挿入します
  • take()キューの先頭を取得して削除します
    必要に応じて、要素が利用可能になるまで待機します

終了時にはsetExit()終了オブジェクトをキューに設定します。take()終了オブジェクトを取得するとスレッドを終了します。

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

sModelExitは終了オブジェクト。

class Model {
    public byte[] data;
}

private LinkedBlockingQueue<Model> 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();
    }
}
このエントリーをはてなブックマークに追加
にほんブログ村 IT技術ブログへ

スポンサードリンク

関連コンテンツ

コメント

メールアドレスが公開されることはありません。 が付いている欄は必須項目です