Executorは別スレッドで非同期処理をいくつも実行するのに役立つ。
ThreadPoolExecutorを使うとスレッドを再利用しつつ非同期処理を連続して行える。
Executor executor = new ThreadPoolExecutor( corePoolSize, //スレッド数 maximumPoolSize, //最大スレッド数 keepAliveTime, //スレッド数がコアより大きい場合、アイドルスレッドが破棄されるまでの時間。 TimeUnit.SECONDS, //KeepAliveTimeの単位 new LinkedBlockingQueue<>()); //Runnableをため込むのキュー executor.execute(()->{ //do something 1 }); executor.execute(()->{ //do something 2 });
個人的によくあるシチュエーションは、
「非同期で実行したい処理が大量にあって、かつ、順次処理したい。」
順次処理なのでスレッドは1つあれば十分。
ということでSerialExecutorなるものを作ったので、自己責任でお持ち帰りください。
仕組みはThreadPoolExecutorをラップしただけ。
あと、強制的にキューの先頭に割り込むexecuteFirstメソッドも装備。
import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.ArrayDeque; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * FIFO処理するExecutor<br/> * {@link #executeFirst(Runnable)} を使用することで、優先実行させることができる */ public class SerialExecutor implements Executor { /** 双方向操作可能なキュー */ private final @NonNull ArrayDeque<Runnable> tasks = new ArrayDeque<>(); /** ラップするExecutor */ private final @NonNull Executor executor; /** カレントRunnable */ private @Nullable Runnable active; /** シングルスレッド生成 */ public SerialExecutor(){ this(1,1,1); } /** * 任意のスレッド生成 * @param corePoolSize 最小スレッド数 * @param maximumPoolSize 最大スレッド数 * @param keepAliveTime スレッド破棄までのアイドル時間(秒数) */ public SerialExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime){ this.executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); } @Override public synchronized void execute(final @NonNull Runnable r) { //offerLastでキューの最後に追加する tasks.offerLast(() -> { try { r.run(); } finally { scheduleNext(); } }); if (active == null) { scheduleNext(); } } /** キューの先頭に追加し優先実行する */ public synchronized void executeFirst(final @NonNull Runnable r) { //offerFirstでキューの先頭に追加する tasks.offerFirst(() -> { try { r.run(); } finally { scheduleNext(); } }); if (active == null) { scheduleNext(); } } /** キューから取り出し実行 */ protected synchronized void scheduleNext() { if ((active = tasks.pollFirst()) != null) { //pollFirstでキューの先頭を取り出す executor.execute(active); } } /** 実行中のRunnableがあるかどうか */ public synchronized boolean isRunning() { return active != null; } }
PriorityQueueを使って優先順位を変化させるようなカスタマイズもできそう。
LIFOにするのも簡単。
この記事を書くのにググりなおしたらSingleThreadExecutorなるものを発見した。
Executors.newSingleThreadExecutor()を使えば良かったみたい。
自分で作る必要無かったw