Java Executors:タスクの優先順位を設定するにはどうすればよいですか?
-
02-10-2019 - |
質問
執行者によって実行されるタスクを優先する可能性はありますか? JCIPで可能性についていくつかの声明を見つけましたが、例を見つけることができず、ドキュメントに関連するものを見つけることができません。
JCIPから:
実行ポリシーは、タスク実行の「何、どこで、いつ、どのように」を指定します。
- ...
- どの順序でタスクを実行する必要があります(FIFO、LIFO、 優先順序)?
- ...
upd: :私は自分が何を尋ねたいのか正確には尋ねなかったことに気付きました。私が本当に欲しかったのは:
設定スレッドの優先順位を使用/エミュレートする方法(つまり、何があったのか thread.setPriority()
)執行者のフレームワークで?
解決
現在、の唯一の具体的な実装 執行者インターフェイス それは ThreadPoolExecutor と スケジュールされたthreadpoolexecutor
ユーティリティ /ファクトリークラスを使用する代わりに 執行者, 、コンストラクターを使用してインスタンスを作成する必要があります。
渡すことができます ブロッキングキュー ThreadPoolexecutorのコンストラクターに。
ブロッキングキューの実装の1つ、 PriorityBlockingQueue コンストラクターにコンパレータを渡すことができます。そうすれば、実行の順序を決定できるようにします。
他のヒント
ここでのアイデアは、執行者にPriorityBlockingQueueを使用することです。このため:
- 私たちの未来を比較するコンパレータを作成します。
- 優先順位を保持するための将来のプロキシを作成します。
- 私たちのプロキシであらゆる将来を包むために、「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
独自の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;
}
}
PriorityブロッキングキューでThreadPoolexecutorを使用できますPriorityBlockingQueueをThreadPoolexecutorおよびカスタムタスクで実装する方法
aを指定できます ThreadFactory
の中に ThreadPoolExecutor
コンストラクター(または Executors
工場の方法)。これにより、執行者に特定のスレッドの優先度のスレッドを提供できます。
異なるジョブの異なるスレッドの優先順位を取得するには、異なるスレッド工場の執行者に送信する必要があります。
setpriority(..)は通常そうしていることに注意してください いいえ Linuxの下で作業します。詳細については、次のリンクを参照してください。
この議論に少し貢献したいだけです。私はこれを実装しました rederingthreadpoolexecutor 非常に具体的な目的のために、私が望むたびに、そして優先順位に対処することなく(この場合はBlockingDequeである)、エグゼキューターのBlockingQueue(この場合はLinkedBlockingDeque)の前面に明示的にもたらすことができます(これは、デッドロックにつながり、とにかく修正されます)。
これを使用して(Androidアプリ内)長いリストビューに表示されている多くの画像をダウンロードする必要があるケースを管理しています。ユーザーがすばやく下にスクロールするたびに、エグゼキューターキューは画像のダウンロード要求に浸水します:最新のリクエストをキューの上に移動することで、実際に画面にある画像を読み込む際にはるかに優れたパフォーマンスを達成し、ダウンロードを遅らせました。おそらく後で必要だったもの。内部同時マップキー(画像URL文字列と同じくらい簡単にすることができる)を使用して、タスクをエグゼキュータに追加して、後で並べ替えるためにそれらを取得できるようにすることに注意してください。
他にも同じことを行う方法はたくさんあり、多分それは複雑であるかもしれませんが、それは正常に機能し、彼のAndroid SDKでFacebookは独自の作業スレッドキューで同様のことをしています。
コードを見て、私に提案をしてください、それはAndroidプロジェクト内にありますが、いくつかのログと注釈を剥奪すると、クラスが純粋なJava 6になります。