2010-07-07 7 views
27

Gibt es eine Möglichkeit, Prioritäten für Aufgaben zu setzen, die von Executors ausgeführt werden? Ich habe einige Aussagen in JCIP darüber gefunden, dass es möglich ist, aber ich kann kein Beispiel finden und ich kann nichts in Docs finden.Java Executors: Wie kann ich Task-Priorität festlegen?

Von JCIP:

Eine Ausführungsrichtlinie gibt die "was, wo, wann und wie" die Aufgabe Ausführung, einschließlich:

  • ...
  • Worin Auftrag sollten Aufgaben ausgeführt werden (FIFO, LIFO, Prioritätsbestellung)?
  • ...

UPD: Ich erkannte, dass ich nicht genau gefragt, was ich fragen wollte. Was ich wirklich wollte, ist:

Wie zu verwenden/emulieren Setting Threads Priorität (d. H. Was war thread.setPriority()) mit Executors Framework?

Antwort

48

derzeit die einzigen konkreten Implementierungen von the Executor interface sind the ThreadPoolExecutor und the ScheduledThreadpoolExecutor

Anstatt das Dienstprogramm/Factory-Klasse der Verwendung Executors, sollten Sie eine Instanz erstellen einen Konstruktor verwenden.

Sie können eine BlockingQueue an die Konstruktoren des ThreadPoolExecutor übergeben.

Eine der Implementierungen der BlockingQueue, the PriorityBlockingQueue, lässt Sie einen Comparator an einen Konstruktor übergeben, damit Sie die Reihenfolge der Ausführung bestimmen können.

+3

+1 PriorityBlockingQueue ist der Weg zu gehen. Sie können einen Komparator implementieren oder die Aufgaben selbst vergleichbar machen. –

+2

Dieser Artikel ist eine sehr gute Referenz: http://binkley.blogspot.fr/2009/04/jumping-work-queue-in-executor.html – Snicolas

+0

Meine Lösung ordnet Aufgaben nach Priorität zu, behält jedoch die Reihenfolge der Übermittlung auf derselben Prioritätsstufe bei: http://StackOverflow.com/a/42831172/1386911 –

0

Bitte beachten Sie, dass setPriority (..) nicht Arbeit unter Linux normalerweise der Fall ist. Auch die folgenden Links für die vollständigen Details:

+2

Kommentare sind Kommentare; Antworten sind Antworten. Kommentare sind keine Antworten. Antworten sind keine Kommentare. Wenn es die gestellte Frage nicht beantwortet, ist es tatsächlich ein Kommentar. –

+0

+1 @Nick - Ha ha, liebe es!Warum ein Wort verwenden, wenn Sie einen langen, sarky Kommentar verwenden könnten. Guter Punkt und gut (frech) gemacht. – TedTrippin

2

Sie ein ThreadFactory im ThreadPoolExecutor Konstruktor angeben kann (oder Executors Factory-Methode). Dadurch können Sie Threads mit einer bestimmten Thread-Priorität für den Executor bereitstellen.

Um verschiedene Thread-Prioritäten für verschiedene Jobs zu erhalten, müssen Sie sie an Executoren mit verschiedenen Threadfactorys senden.

30

Die Idee hier ist die Verwendung einer PriorityBlockingQueue im Executor. Dazu:

  • Erstellen Sie einen Vergleich, der unsere Zukunft vergleichen würde.
  • Erstellen Sie einen Proxy für die Zukunft, um eine Priorität zu halten.
  • Überschreiben Sie 'newTaskFor', um jede Zukunft in unserem Proxy zu verpacken.

Zuerst müssen Sie Priorität auf Ihre Zukunft halten:

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(); 
    } 
} 

Weiter Sie Vergleicher definieren müssen, die korrekt die Priorität Futures sortieren würde:

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); 
     } 
    } 
} 

Als nächstes nehmen wir an, wir haben langwieriger Job wie folgt:

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; 
    } 
} 

dann, um der Code diese Aufträge in der Priorität auszuführen wie folgt aussehen:

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); 
     } 
    } 
} 

Das ist eine Menge Code ist, aber das ist fast der einzige Weg, dies erreicht werden kann.

Auf meinem Rechner ist die Ausgabe wie folgt aus:

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 
+0

Während die * angenommene Antwort * die Frage beantwortet, liefert diese eine funktionierende Lösung. Danke vielmals. – m02ph3u5

+0

Danke für Ihre Antwort. Wäre es möglich, diesen Ansatz mit ExecutorCompletionService zu verwenden? Ich habe versucht, das ExecutorService-Objekt in dem ExecutorCompletionService-Konstruktor zu übergeben, aber das Ergebnis kann nicht zu PriorityFuture in dem Vergleicher übergeben werden. – Arash

+0

Ich habe an meinem Rechner getestet. Es ist nicht richtig. Auf meinem Rechner habe ich Executing 72 vor Executing 3, was eindeutig falsch ist. –

0

Ich will nur mein bisschen Beitrag zu dieser Diskussion hinzufügen. Ich habe diese ReorderingThreadPoolExecutor für einen ganz bestimmten Zweck implementiert, der in der Lage ist, die BlockingQueue des Executors (in diesem Fall LinkedBlockingDeque) immer an die Front zu bringen, wann immer ich will und ohne mit Prioritäten umgehen zu müssen (was zu Deadlocks führen kann) jedenfalls fest).

Ich verwende dies, um (in einer Android-App) den Fall zu verwalten, in dem ich viele Bilder herunterladen muss, die in einer langen Listenansicht angezeigt werden. Immer wenn der Benutzer schnell herunterscrollt, wird die Executor-Warteschlange von Bild-Download-Anfragen überflutet: durch Verschieben der letzten an der Spitze der Warteschlange, habe ich viel bessere Leistungen beim Laden der Bilder erreicht, die tatsächlich auf dem Bildschirm sind und den Download verzögern diejenigen, die wahrscheinlich später benötigt werden. Beachten Sie, dass ich einen internen Concurrent-Map-Schlüssel (der so einfach wie der Bild-URL-String sein kann) verwende, um die Tasks dem Executor hinzuzufügen, damit ich sie später für die Neuanordnung abrufen kann.

Es hätte viele andere Möglichkeiten gegeben, dasselbe zu machen und vielleicht ist es zu kompliziert, aber es funktioniert gut und auch Facebook in seinem Android SDK macht etwas ähnliches in seiner eigenen Working-Thread-Warteschlange.

Fühlen Sie sich frei, einen Blick auf den Code zu haben und mir Anregungen geben, es in einem Android-Projekt ist, aber ein paar Protokolle und Anmerkungen Strippen würde die Klasse reine Java 6.

0

Sie können Ihre eigenen Thread und stellen Sie machen implementieren es innerhalb ThreadPoolExecutor wie folgt aus:

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

wo mein OpJobThreadFactory wie folgt aussieht:

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; 
    } 
} 
Verwandte Themen