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