web-dev-qa-db-ja.com

Java Executors:タスクの優先度を設定するにはどうすればよいですか?

エグゼキューターによって実行されるタスクに優先順位を設定する可能性はありますか? JCIPで可能なことについていくつかのステートメントを見つけましたが、例が見つからず、ドキュメントに関連するものが見つかりません。

JCIPから:

実行ポリシーは、タスク実行の「何、どこ、いつ、どのように」を指定します。

  • ...
  • どの順序でタスクを実行する必要がありますか(FIFO、LIFO、優先順位)?
  • ...

[〜#〜] upd [〜#〜]:私は自分が何を聞きたいのか正確に尋ねていないことに気付きました。私が本当に欲しかったのは:

Executorsフレームワークでスレッドの優先度の設定(つまり、thread.setPriority()でした)を使用/エミュレートする方法は?

37
Roman

現在、 Executorインターフェース の唯一の具体的な実装は ThreadPoolExecutor および ScheduledThreadpoolExecutor です

ユーティリティ/ファクトリクラス Executors を使用する代わりに、コンストラクターを使用してインスタンスを作成する必要があります。

BlockingQueue をThreadPoolExecutorのコンストラクターに渡すことができます。

BlockingQueueの実装の1つである the PriorityBlockingQueue を使用すると、Comparatorをコンストラクターに渡すことができるため、実行の順序を決定できます。

58
Davy Meers

ここでのアイデアは、executorでPriorityBlockingQueueを使用することです。このため:

  • 先物を比較するコンパレータを作成します。
  • Futureのプロキシを作成して、優先順位を保持します。
  • プロキシですべての未来をラップするには、「newTaskFor」をオーバーライドします。

最初に、将来を優先する必要があります。

    class PriorityFuture<T> implements RunnableFuture<T> {

    private RunnableFuture<T> src;
    private int priority;

    public PriorityFuture(RunnableFuture<T> other, int priority) {
        this.src = other;
        this.priority = priority;
    }

    public int getPriority() {
        return priority;
    }

    public boolean cancel(boolean mayInterruptIfRunning) {
        return src.cancel(mayInterruptIfRunning);
    }

    public boolean isCancelled() {
        return src.isCancelled();
    }

    public boolean isDone() {
        return src.isDone();
    }

    public T get() throws InterruptedException, ExecutionException {
        return src.get();
    }

    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return src.get();
    }

    public void run() {
        src.run();
    }
}

次に、優先先物を正しくソートするコンパレータを定義する必要があります。

class PriorityFutureComparator implements Comparator<Runnable> {
    public int compare(Runnable o1, Runnable o2) {
        if (o1 == null && o2 == null)
            return 0;
        else if (o1 == null)
            return -1;
        else if (o2 == null)
            return 1;
        else {
            int p1 = ((PriorityFuture<?>) o1).getPriority();
            int p2 = ((PriorityFuture<?>) o2).getPriority();

            return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
        }
    }
}

次に、次のような長い仕事があると仮定します。

class LenthyJob implements Callable<Long> {
    private int priority;

    public LenthyJob(int priority) {
        this.priority = priority;
    }

    public Long call() throws Exception {
        System.out.println("Executing: " + priority);
        long num = 1000000;
        for (int i = 0; i < 1000000; i++) {
            num *= Math.random() * 1000;
            num /= Math.random() * 1000;
            if (num == 0)
                num = 1000000;
        }
        return num;
    }

    public int getPriority() {
        return priority;
    }
}

次に、これらのジョブを優先して実行するためのコードは次のようになります。

public class TestPQ {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        int nThreads = 2;
        int qInitialSize = 10;

        ExecutorService exec = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
                new PriorityBlockingQueue<Runnable>(qInitialSize, new PriorityFutureComparator())) {

            protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
                RunnableFuture<T> newTaskFor = super.newTaskFor(callable);
                return new PriorityFuture<T>(newTaskFor, ((LenthyJob) callable).getPriority());
            }
        };

        for (int i = 0; i < 20; i++) {
            int priority = (int) (Math.random() * 100);
            System.out.println("Scheduling: " + priority);
            LenthyJob job = new LenthyJob(priority);
            exec.submit(job);
        }
    }
}

これは大量のコードですが、それがこれを達成できる唯一の方法です。

私のマシンでは、出力は次のようになります。

Scheduling: 39
Scheduling: 90
Scheduling: 88
Executing: 39
Scheduling: 75
Executing: 90
Scheduling: 15
Scheduling: 2
Scheduling: 5
Scheduling: 24
Scheduling: 82
Scheduling: 81
Scheduling: 3
Scheduling: 23
Scheduling: 7
Scheduling: 40
Scheduling: 77
Scheduling: 49
Scheduling: 34
Scheduling: 22
Scheduling: 97
Scheduling: 33
Executing: 2
Executing: 3
Executing: 5
Executing: 7
Executing: 15
Executing: 22
Executing: 23
Executing: 24
Executing: 33
Executing: 34
Executing: 40
Executing: 49
Executing: 75
Executing: 77
Executing: 81
Executing: 82
Executing: 88
Executing: 97
38

独自のThreadFactoryを実装し、次のようにThreadPoolExecutor内で設定できます。

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, numOfWorkerThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.setThreadFactory(new OpJobThreadFactory(Thread.NORM_PRIORITY-2));

opJobThreadFactoryは次のようになります。

public final static class OpJobThreadFactory implements ThreadFactory {
   private int priority;
   private boolean daemon;
   private final String namePrefix;
   private static final AtomicInteger poolNumber = new AtomicInteger(1);
   private final AtomicInteger threadNumber = new AtomicInteger(1);

   public OpJobThreadFactory(int priority) {
      this(priority, true);
   }

   public OpJobThreadFactory(int priority, boolean daemon) {
      this.priority = priority;
      this.daemon = daemon;
      namePrefix = "jobpool-" +poolNumber.getAndIncrement() + "-thread-";
   }

   @Override
   public Thread newThread(Runnable r) {
      Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
      t.setDaemon(daemon);
      t.setPriority(priority);
      return t;
   }
}
4
dfreis

priorityPlockキューでThreadPoolExecutorを使用できます ThreadPoolExecutorおよびカスタムタスクでPriorityBlockingQueueを実装する方法

3
dupdup

ThreadFactoryコンストラクター(またはThreadPoolExecutorファクトリーメソッド)で Executors を指定できます。これにより、エグゼキューターに特定のスレッド優先度のスレッドを提供できます。

ジョブごとに異なるスレッド優先順位を取得するには、異なるスレッドファクトリーを持つエグゼキューターに送信する必要があります。

この議論に私のちょっとした貢献を加えたいだけです。私はこれを実装しました ReorderingThreadPoolExecutor 非常に特定の目的のために、優先順位に対処する必要なく、いつでもエグゼキューターのBlockingQueue(この場合はLinkedBlockingDeque)の前に明示的に持ってくることができます(デッドロックにつながる可能性があり、とにかく修正されます)。

これを使用して(Androidアプリ内)長いリストビューに表示される多くの画像をダウンロードする必要がある場合を管理します。ユーザーがすばやくスクロールダウンするたびに、executorキュー画像ダウンロードリクエストが殺到します。最新のものをキューの一番上に移動することで、実際に画面上にある画像の読み込みのパフォーマンスが大幅に向上し、後で必要になる可能性のあるもののダウンロードが遅れます。内部コンカレントマップキー(イメージURL文字列と同じくらい簡単な場合もあります)を使用してタスクをエグゼキューターに追加し、後で並べ替えのためにそれらを取得できるようにします。

同じことを行う他の多くの方法があり、おそらく複雑すぎますが、それはうまく機能し、FacebookのAndroid SDKは独自の作業スレッドキューで同様のことをしています。

コードを見て自由に提案してください、それはAndroidプロジェクト内にありますが、いくつかのログと注釈を取り除くとクラスは純粋になりますJava 6。

0
fast3r

LinuxでsetPriority(..)は通常not動作しないことに注意してください。詳細については、次のリンクを参照してください。

0
aanno