Comment puis-je mettre en œuvre la priorisation des tâches à l'aide d'un ExecutorService en Java 5?
Je suis d'exécution d'un thread mécanisme de mise en commun dans lequel j'aimerais exécuter des tâches de différentes priorités. J'aimerais avoir une belle mécanisme par lequel je peux soumettre une tâche hautement prioritaire pour le service avant de l'être programmées avant d'autres tâches. La priorité de la tâche est une propriété intrinsèque de la tâche elle-même (si j'exprime que la tâche est un Callable
ou un Runnable
n'est pas important pour moi).
Maintenant, superficiellement, il semble que je pourrais utiliser un PriorityBlockingQueue
que la tâche de la file d'attente à mon ThreadPoolExecutor
, mais que la file d'attente contient Runnable
objets, ce qui peut ou peut ne pas être le Runnable
tâches que j'ai soumis. De plus, si je l'ai soumis Callable
tâches, il n'est pas clair comment cela serait jamais la carte.
Est-il un moyen de faire cela? J'aurais vraiment préféré ne pas rouler mes propres pour cela, depuis que je suis beaucoup plus susceptibles de se tromper de cette façon.
(D'un côté, oui, je suis conscient de la possibilité de la famine pour des travaux prioritaires dans quelque chose comme cela. Des points supplémentaires (?!) pour des solutions qui ont une garantie raisonnable de l'équité)
- Question intéressante. Cela semble être un peu d'une surveillance dans l'API, à mon avis.
- Si je devais deviner pourquoi il ne fait pas partie de l'API, je dirais que c'est probablement parce que la faim est une question délicate. Ils avaient besoin de fournir un nouvel ensemble de primitives pour l'équité et de l'escalade; des choses comme doit-exécuter et peut-être indéfiniment différée (note que je suis en tirant ces noms de mon cul). Je pourrais souhaiter qu'ils avaient fait, mais je ne leur en veux pas 🙂
- Ouais, ça a du sens. Semble que ce serait une bonne chose à avoir, même si, mais quand vous pensez que vous avez besoin essentiellement d'écrire un PROCESSEUR algorithme d'ordonnancement en Java, vous êtes probablement fait quelque chose de mal.
- JDK 6 corrige cette omission, en offrant de nouvelles newTaskFor méthodes de AbstractExecutorService qui vous permettent de contrôler la façon dont le soumis tâches sont enveloppés, et ainsi de vous permettre de revenir instances qui sont Comparables et peuvent être facilement commandés par un PriorityQueue.
Vous devez vous connecter pour publier un commentaire.
À première vue, il semblerait, vous pouvez définir une interface pour vos tâches qui s'étend
Runnable
ouCallable<T>
etComparable
. Puis les envelopper d'uneThreadPoolExecutor
avec unPriorityBlockingQueue
que la file d'attente, et seulement accepter les tâches qui implémentent l'interface.De prendre votre commentaire en compte, il ressemble à une option consiste à étendre
ThreadPoolExecutor
, et de remplacer lessubmit()
méthodes. Reportez-vous àAbstractExecutorService
pour voir ce que ceux par défaut ressembler; tout ce qu'ils font est de conclure laRunnable
ouCallable
dans unFutureTask
etexecute()
il. Je serais probablement le faire par écrit au moyen d'un wrapper de la classe qui implémenteExecutorService
et les délégués à un anonyme intérieureThreadPoolExecutor
. Les envelopper dans quelque chose qui est votre priorité, de sorte que votreComparator
pouvez obtenir à elle.Runnable
instances qui sont passés dans la file d'attente de priorité sont pas les tâches que j'aisubmit
directement, ils sont enveloppés dans unjava.util.concurrent.FutureTask<V>
qui n'est évidemment pas triées de la même façon. Si j'utiliseexecute
-- qui n'a pas, par exemple, accepterCallable
-- et puis il jette mes propres objets.J'ai résolu ce problème d'une façon raisonnable, et je vais vous décrire ci-dessous pour référence future à moi-même et personne d'autre qui s'exécute ce problème avec Java Simultanées bibliothèques.
À l'aide d'un
PriorityBlockingQueue
que les moyens de les accrocher à des tâches pour l'exécution ultérieure est en fait un mouvement dans la bonne direction. Le problème est que lePriorityBlockingQueue
doit être générique instancié pour contenirRunnable
cas, et il est impossible d'appelercompareTo
(ou similaire) sur unRunnable
interface.Sur la résolution du problème. Lors de la création de l'Exécuteur testamentaire, il doit être donné une
PriorityBlockingQueue
. La file d'attente doit être donné une coutume Comparateur d'effectuer correctement en place le tri:Maintenant, un coup d'oeil à
CustomTaskComparator
:Tout semble assez simple jusqu'à ce point. Cela devient un peu collant ici. Notre prochain problème est de traiter avec la création de FutureTasks de l'Exécuteur testamentaire. À l'Exécuteur, nous devons remplacer
newTaskFor
de la manière suivante:Où
c
est leCallable
tâche que nous essayons de nous exécuter. Maintenant, nous allons jeter un coup d'oeil àCustomFutureTask
:Avis de la
getTask
méthode. Nous allons l'utiliser plus tard pour attraper la tâche d'origine de cetteCustomFutureTask
que nous avons créé.Et enfin, nous allons modifier la tâche d'origine que nous avons tenté de l'exécuter:
Vous pouvez voir que nous mettons en œuvre
Comparable
dans la tâche à déléguer à la réelleComparator
pourMyType
.Et là vous l'avez, personnalisé, de la hiérarchisation de l'Exécuteur testamentaire, en utilisant les bibliothèques Java! Il prend un peu de flexion, mais c'est le plus propre que j'ai été en mesure de venir avec. J'espère que c'est utile à quelqu'un!
Vous pouvez utiliser ces classes d'assistance:
ET
ET cette méthode d'assistance:
ET puis l'utiliser comme ceci:
Je vais essayer d'expliquer ce problème avec un code fonctionnel. Mais avant de plonger dans le code, je tiens à expliquer à propos de PriorityBlockingQueue
PriorityBlockingQueue : PriorityBlockingQueue est une implémentation de BlockingQueue. Il accepte les tâches ainsi que leur priorité et soumet la tâche avec la plus haute priorité pour l'exécution de la première. Si deux tâches ont la même priorité, nous devons fournir de l'logique personnalisée pour décider de la tâche qui va en premier.
Maintenant permet d'obtenir dans le code tout de suite.
De la classe du pilote : Cette classe crée un exécuteur testamentaire qui accepte des tâches et, plus tard, les soumet pour l'exécution. Ici, nous créons deux tâches, l'une avec une FAIBLE priorité et l'autre avec une priorité ÉLEVÉE. Ici nous raconter l'exécuteur testamentaire pour exécuter un maximum de 1 fils et de l'utilisation de la PriorityBlockingQueue.
MyTask classe : MyTask implements Runnable et accepte de priorité comme un argument dans le constructeur. Lors de l'exécution de cette tâche, il imprime un message, puis met le fil de dormir pendant 1 seconde.
MyFutureTask classe : Puisque nous sommes à l'aide de PriorityBlocingQueue pour la tenue de nos tâches, nos tâches doivent être enveloppé à l'intérieur FutureTask et de mise en œuvre de notre FutureTask doit implémenter l'interface Comparable. L'interface Comparable compare la priorité de 2 tâches différentes et soumet la tâche avec la plus haute priorité pour l'exécution.
Classe de priorité : Auto explicative la classe de Priorité.
Maintenant, quand nous exécuter cet exemple, nous obtenons le résultat suivant
Même si nous avons soumis à la FAIBLE priorité première, mais la grande priorité de la tâche à plus tard, mais puisque nous sommes à l'aide d'un PriorityBlockingQueue, n'importe quelle tâche avec une priorité plus élevée sera exécuté en premier.
Ma solution préserve submition ordre des tâches pour les mêmes priorités. C'est une amélioration de cette réponse
L'exécution de la tâche afin est basée sur:
Testeur de classe:
Résultat:
La solution:
Serait-il possible d'avoir un ThreadPoolExecutor pour chaque niveau de priorité? Un ThreadPoolExecutor peut être instanciée avec un ThreadFactory et vous pourriez avoir votre propre mise en œuvre d'un ThreadFactory pour définir les différents niveaux de priorité.