365連休

にわかのandroidとかの開発メモ。

Android Studio 3.5 直列処理Executorのサンプル

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なるものを発見した。

qiita.com

 

Executors.newSingleThreadExecutor()を使えば良かったみたい。

自分で作る必要無かったw