私はたくさん検索しましたが、私の問題の解決策を見つけることができませんでした。
BaseTask
を使用してタスクを処理する独自のクラスThreadPoolExecutor
があります。タスクの優先順位付けが必要ですが、PriorityBlockingQueue
を使用しようとするとClassCastException
が取得されます。これはThreadPoolExecutor
がタスクをFutureTask
オブジェクトにラップするためです。
FutureTask
はComparable
を実装していないため、これは明らかに理にかなっていますが、優先度の問題を解決するにはどうすればよいでしょうか。 ThreadPoolExecutor
のnewTaskFor()
をオーバーライドできることを読みましたが、このメソッドがまったく見つからないようです...?
どんな提案でも大歓迎です!
役立つコード:
私のBaseTask
クラスには
private static final BlockingQueue<Runnable> sWorkQueue = new PriorityBlockingQueue<Runnable>();
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BaseThreadPoolExecutor sExecutor = new BaseThreadPoolExecutor(
1, Integer.MAX_VALUE, 10, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private final BaseFutureTask<Result> mFuture;
public BaseTask(int priority) {
mFuture = new BaseFutureTask<Result>(mWorker, priority);
}
public final BaseTask<Params, Progress, Result> execute(Params... params) {
/* Some unimportant code here */
sExecutor.execute(mFuture);
}
BaseFutureTask
クラス内
@Override
public int compareTo(BaseFutureTask another) {
long diff = this.priority - another.priority;
return Long.signum(diff);
}
BaseThreadPoolExecutor
クラスで、3つのsubmit
メソッドをオーバーライドします...このクラスのコンストラクターが呼び出されますが、submit
メソッドはありません
public class ExecutorPriority {
public static void main(String[] args) {
PriorityBlockingQueue<Runnable> pq = new PriorityBlockingQueue<Runnable>(20, new ComparePriority());
Executor exe = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, pq);
exe.execute(new RunWithPriority(2) {
@Override
public void run() {
System.out.println(this.getPriority() + " started");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(ExecutorPriority.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(this.getPriority() + " finished");
}
});
exe.execute(new RunWithPriority(10) {
@Override
public void run() {
System.out.println(this.getPriority() + " started");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(ExecutorPriority.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(this.getPriority() + " finished");
}
});
}
private static class ComparePriority<T extends RunWithPriority> implements Comparator<T> {
@Override
public int compare(T o1, T o2) {
return o1.getPriority().compareTo(o2.getPriority());
}
}
}
ご想像のとおり、RunWithPriorityは、実行可能で整数の優先度フィールドを持つ抽象クラスです。
次のヘルパークラスを使用できます。
public 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();
}
public static Comparator<Runnable> COMP = new 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);
}
}
};
}
[〜#〜]および[〜#〜]
public interface PriorityCallable<T> extends Callable<T> {
int getPriority();
}
[〜#〜] and [〜#〜]このヘルパーメソッド:
public static ThreadPoolExecutor getPriorityExecutor(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(10, PriorityFuture.COMP)) {
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
RunnableFuture<T> newTaskFor = super.newTaskFor(callable);
return new PriorityFuture<T>(newTaskFor, ((PriorityCallable<T>) callable).getPriority());
}
};
}
[〜#〜] and [〜#〜]次に、次のように使用します。
class LenthyJob implements PriorityCallable<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 {
ThreadPoolExecutor exec = getPriorityExecutor(2);
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);
}
}
}
この問題を完全に機能するコードで説明しようと思います。しかし、コードに飛び込む前に、PriorityBlockingQueueについて説明したいと思います。
PriorityBlockingQueue:PriorityBlockingQueueはBlockingQueueの実装です。タスクを優先度とともに受け入れ、優先度が最も高いタスクを最初に実行のために送信します。 2つのタスクの優先度が同じである場合は、どちらのタスクを最初に実行するかを決定するためのカスタムロジックを提供する必要があります。
それでは、すぐにコードに取り掛かりましょう。
ドライバークラス:このクラスは、タスクを受け入れ、後で実行のために送信するエグゼキューターを作成します。ここでは、優先度が低いタスクと優先度が高いタスクの2つのタスクを作成します。ここでは、エグゼキュータに最大1スレッドを実行し、PriorityBlockingQueueを使用するように指示します。
public static void main(String[] args) {
/*
Minimum number of threads that must be running : 0
Maximium number of threads that can be created : 1
If a thread is idle, then the minimum time to keep it alive : 1000
Which queue to use : PriorityBlockingQueue
*/
PriorityBlockingQueue queue = new PriorityBlockingQueue();
ThreadPoolExecutor executor = new ThreadPoolExecutor(0,1,
1000, TimeUnit.MILLISECONDS,queue);
MyTask task = new MyTask(Priority.LOW,"Low");
executor.execute(new MyFutureTask(task));
task = new MyTask(Priority.HIGH,"High");
executor.execute(new MyFutureTask(task));
}
MyTaskクラス:MyTaskはRunnableを実装し、コンストラクターの引数として優先度を受け入れます。このタスクが実行されると、メッセージが出力され、スレッドが1秒間スリープ状態になります。
public class MyTask implements Runnable {
public int getPriority() {
return priority.getValue();
}
private Priority priority;
public String getName() {
return name;
}
private String name;
public MyTask(Priority priority,String name){
this.priority = priority;
this.name = name;
}
@Override
public void run() {
System.out.println("The following Runnable is getting executed "+getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyFutureTaskクラス:タスクを保持するためにPriorityBlocingQueueを使用しているため、タスクをFutureTask内にラップし、FutureTaskの実装でComparableインターフェイスを実装する必要があります。 Comparableインターフェースは、2つの異なるタスクの優先度を比較し、実行のために最も優先度の高いタスクを送信します。
public class MyFutureTask extends FutureTask<MyFutureTask>
implements Comparable<MyFutureTask> {
private MyTask task = null;
public MyFutureTask(MyTask task){
super(task,null);
this.task = task;
}
@Override
public int compareTo(MyFutureTask another) {
return task.getPriority() - another.task.getPriority();
}
}
優先クラス:自明の優先クラス。
public enum Priority {
HIGHEST(0),
HIGH(1),
MEDIUM(2),
LOW(3),
LOWEST(4);
int value;
Priority(int val) {
this.value = val;
}
public int getValue(){
return value;
}
}
この例を実行すると、次の出力が得られます。
The following Runnable is getting executed High
The following Runnable is getting executed Low
最初にLOW優先度を送信し、後でHIGH優先度タスクを送信しましたが、PriorityBlockingQueueを使用しているため、優先度の高いタスクが最初に実行されます。
私の解決策:
public class XThreadPoolExecutor extends ThreadPoolExecutor
{
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value)
{
return new ComparableFutureTask<>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable)
{
return new ComparableFutureTask<>(callable);
}
protected class ComparableFutureTask<V>
extends FutureTask<V> implements Comparable<ComparableFutureTask<V>>
{
private Object object;
public ComparableFutureTask(Callable<V> callable)
{
super(callable);
object = callable;
}
public ComparableFutureTask(Runnable runnable, V result)
{
super(runnable, result);
object = runnable;
}
@Override
@SuppressWarnings("unchecked")
public int compareTo(ComparableFutureTask<V> o)
{
if (this == o)
{
return 0;
}
if (o == null)
{
return -1; // high priority
}
if (object != null && o.object != null)
{
if (object.getClass().equals(o.object.getClass()))
{
if (object instanceof Comparable)
{
return ((Comparable) object).compareTo(o.object);
}
}
}
return 0;
}
}
}
質問に答えるには:newTaskFor()
メソッドはThreadPoolExecutor
のスーパークラスAbstractExecutorService
にあります。ただし、ThreadPoolExecutor
で簡単にオーバーライドできます。
彼らはそれをApacheの調和から外したようです。 svn commit log 約1年前に、newTaskFor
の不在を修正しています。おそらく、拡張submit
のThreadPoolExecutor
関数をオーバーライドして、FutureTask
である拡張Comparable
を作成することができます。 それほど長くはありません 。
この回答は、@ StanislavVitvitskyyの回答を簡略化したものです。彼に感謝します。
提出したjobsをComparable
にしたかったのです。 ExecutorService
を使用してPriorityBlockingQueue
を作成し、それを拡張してnewTaskFor(...)
メソッドを処理しました。
_ExecutorService pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, timeUnit, new PriorityBlockingQueue<Runnable>()) {
@Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new ComparableFutureTask<T>(runnable, value);
}
@Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ComparableFutureTask<T>(callable);
};
};
_
プールに送信されるjob.compareTo(...)
に委任することにより、ComparableFutureTask
を拡張し、FutureTask
を実装するComparable
を定義しました。
_public class ComparableFutureTask<T> extends FutureTask<T>
implements Comparable<Object> {
private final Comparable<Object> comparableJob;
@SuppressWarnings("unchecked")
public ComparableFutureTask(Runnable runnable, T value) {
super(runnable, value);
this.comparableJob = (Comparable<Object>) runnable;
}
@SuppressWarnings("unchecked")
public ComparableFutureTask(Callable<T> callable) {
super(callable);
this.comparableJob = (Comparable<Object>) callable;
}
@Override
public int compareTo(Object o) {
return this.comparableJob
.compareTo(((ComparableFutureTask<?>) o).comparable);
}
}
_
このExecutorService
は、Runnable
でもあるCallable
またはComparable
ジョブを処理できます。例えば:
_public class MyJob implements Runnable, Comparable<MyJob> {
private int priority;
...
@Override
public int compareTo(MyJob other) {
// we want higher priority to go first
return other.priority - this.priority;
}
...
}
_
Comparable
以外のジョブをこのキューに送信すると、ClassCastException
がスローされることに注意してください。