2010-07-07 18 views
27

Est-il possible de définir la priorité pour les tâches exécutées par Executors? J'ai trouvé quelques déclarations dans JCIP à propos de ce qui est possible mais je ne trouve aucun exemple et je ne trouve rien dans les docs.Exécutifs Java: comment définir la priorité de la tâche?

De JCIP:

Une politique d'exécution précise le "quoi, où, quand et comment" d'exécution d'une tâche , y compris:

  • ...
  • Dans ce l'ordre doit-il être exécuté (FIFO, LIFO, ordre de priorité)?
  • ...

UPD: Je réalisais que je demandais pas exactement ce que je voulais poser. Ce que je voulais vraiment, c'est:

Comment utiliser/émuler la priorité des fils de réglage (c'est-à-dire ce qui était thread.setPriority()) avec la structure des exécutants?

Répondre

48

Actuellement, les seules mises en œuvre concrètes de the Executor interface sont the ThreadPoolExecutor et the ScheduledThreadpoolExecutor

Au lieu d'utiliser la classe utilitaire/usine Executors, vous devez créer une instance à l'aide d'un constructeur.

Vous pouvez transmettre un BlockingQueue aux constructeurs du ThreadPoolExecutor.

L'une des implémentations de la BlockingQueue, the PriorityBlockingQueue vous permet de passer un comparateur à un constructeur, ce qui vous permet de décider de l'ordre d'exécution.

+3

+1 PriorityBlockingQueue est le chemin à parcourir. Vous pouvez implémenter un comparateur ou rendre les tâches comparables. –

+2

Cet article est une excellente référence: http://binkley.blogspot.fr/2009/04/jumping-work-queue-in-executor.html – Snicolas

+0

Ma solution ordonne les tâches par priorité, tout en conservant l'ordre de soumission aux mêmes niveaux de priorité: http://stackoverflow.com/a/42831172/1386911 –

0

S'il vous plaît être conscient que setPriority (..) ne normalement pas travail sous Linux. Voir les liens ci-dessous pour tous les détails:

+2

Les commentaires sont des commentaires; les réponses sont des réponses. Les commentaires ne sont pas des réponses. Les réponses ne sont pas des commentaires. Si cela ne répond pas à la question posée, c'est en fait un commentaire. –

+0

+1 @Nick - Ha ha, j'adore!Pourquoi utiliser un mot quand vous pourriez utiliser un long, commentaire sarky. Bon point et bien (effrontément) fait. – TedTrippin

2

Vous pouvez spécifier un ThreadFactory dans le constructeur ThreadPoolExecutor (ou Executors méthode d'usine). Cela vous permet de fournir des threads d'une priorité de thread donnée pour l'exécuteur.

Pour obtenir différentes priorités de threads pour différents travaux, vous devez les envoyer à des exécuteurs disposant de différentes usines de threads.

30

L'idée ici est d'utiliser une PriorityBlockingQueue dans l'exécuteur. Pour cela:

  • Créez un comparateur qui comparerait nos futures.
  • Créez un proxy pour l'avenir pour conserver une priorité.Remplacez «newTaskFor» par «newTaskFor» afin d'envelopper chaque avenir dans notre proxy.

Vous devez d'abord tenir la priorité sur votre avenir:

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

Ensuite, vous devez définir comparateur qui correctement trier les futures prioritaires:

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

Ensuite, nous allons supposer que nous avons travail de longue durée comme ceci:

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

Ensuite, afin d'exécuter ces travaux en priorité le code ressemblera:

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

C'est beaucoup de code, mais qui est à peu près la seule façon cela peut être accompli.

Sur ma machine, la sortie est comme ce qui suit:

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

Alors que la * réponse acceptée * répond à la question, celle-ci fournit une solution de travail. Merci beaucoup. – m02ph3u5

+0

Merci pour votre réponse. Serait-il possible d'utiliser cette approche avec ExecutorCompletionService? J'ai essayé de transmettre l'objet ExecutorService dans le constructeur ExecutorCompletionService mais le résultat ne peut pas être converti en PriorityFuture dans le comparateur. – Arash

+0

J'ai testé sur ma machine. Ce n'est pas correct. Sur ma machine, j'ai exécuté 72 avant d'exécuter 3, ce qui est tout à fait faux. –

0

veux juste ajouter mon peu de contribution à cette discussion. J'ai implémenté ce ReorderingThreadPoolExecutor dans un but très spécifique, qui est de pouvoir explicitement placer au début de BlockingQueue de l'exécuteur (dans ce cas LinkedBlockingDeque) quand je veux et sans avoir à gérer les priorités (qui peuvent conduire à des blocages et sont , de toute façon, fixe). J'utilise ceci pour gérer (dans une application Android) le cas où je dois télécharger de nombreuses images qui sont affichées dans une longue liste. Chaque fois que l'utilisateur descend rapidement, la file d'attente de l'exécuteur est submergée de demandes de téléchargement d'images: en déplaçant les dernières en haut de la file, j'ai obtenu de bien meilleures performances en chargeant les images qui apparaissent sur l'écran. ceux qui auront probablement besoin plus tard. Notez que j'utilise une clé de carte concurrente interne (qui peut être aussi simple que la chaîne d'URL de l'image) pour ajouter les tâches à l'exécuteur afin que je puisse les récupérer plus tard pour la réorganisation.

Il y aurait eu beaucoup d'autres façons de faire la même chose et peut-être que c'est compliqué, mais ça fonctionne bien et Facebook dans son SDK Android fait quelque chose de similaire dans sa propre file d'attente.

Ne hésitez pas à jeter un oeil sur le code et me donner des suggestions, il est à l'intérieur d'un projet Android, mais stripping quelques journaux et annotations rendrait la pure classe Java 6.

0

Vous pouvez implémenter votre propre ThreadFactory et mis dans un délai ThreadPoolExecutor comme ceci:

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

où mon OpJobThreadFactory se présente comme suit:

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