File d'attente simultanée et bloquante en Java

, j'ai le problème classique d'un thread en poussant des événements de la file d'attente entrante d'un deuxième thread. Seulement cette fois, je suis très intéressé par la performance. Ce que je veux réaliser est:

  • je veux un accès simultané à la file d'attente, le producteur poussant, le récepteur poping.
  • Lorsque la file d'attente est vide, je veux que le consommateur bloque la file d'attente, en attendant le producteur.

Ma première idée était d'utiliser un LinkedBlockingQueue, mais j'ai vite réalisé que ce n'était pas simultané et la performance a souffert. D'un autre côté, j'utilise maintenant un ConcurrentLinkedQueue, mais je paie toujours le coût de wait() / notify() sur chaque publication. Puisque le consommateur, en trouvant une file d'attente vide, ne bloque pas, je dois synchroniser et wait() sur un verrou. D'autre part, le producteur doit obtenir ce verrou et notify() à chaque publication. Le résultat global est que je paie le coût de sycnhronized (lock) {lock.notify()} dans chaque publication, même si elle n'est pas nécessaire.

Ce que je suppose est nécessaire ici, est un file d'attente qui est à la fois bloquante et concurrente. J'imagine une opération push() pour fonctionner comme dans ConcurrentLinkedQueue, avec un notify() supplémentaire à l'objet lorsque l'élément poussé est le premier de la liste. Une telle vérification que je considère déjà exister dans le ConcurrentLinkedQueue, car pousser nécessite une connexion avec l'élément suivant. Ainsi, ce serait beaucoup plus rapide que la synchronisation à chaque fois sur le verrou externe.

Quelque chose comme ça est disponible / raisonnable?

26
demandé sur cegas 2009-07-31 16:55:42

6 réponses

Je pense que vous pouvez coller à java.util.concurrent.LinkedBlockingQueue indépendamment de vos doutes. Il est simultanées. Cependant, je n'ai aucune idée de ses performances. Probablement, une autre implémentation de BlockingQueue vous conviendra mieux. Il n'y en a pas trop, alors faites des tests de performance et mesurez.

11
répondu Rorick 2016-12-02 04:36:08

Semblable à cette réponse https://stackoverflow.com/a/1212515/1102730, mais un peu différent.. J'ai fini par utiliser un ExecutorService. Vous pouvez en instancier un en utilisant Executors.newSingleThreadExecutor(). J'avais besoin d'une file d'attente simultanée pour lire/écrire des BufferedImages dans des fichiers, ainsi que de l'atomicité avec des lectures et des Écritures. J'ai seulement besoin d'un seul thread car le fichier IO est plus rapide que la source, net IO. En outre, j'étais plus préoccupé par l'atomicité des actions et l'exactitude que la performance, mais cela l'approche peut également être faite avec plusieurs threads dans le pool pour accélérer les choses.

Pour obtenir une image (Try-Catch-finalement omis):

Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
    @Override
        public BufferedImage call() throws Exception {
            ImageInputStream is = new FileImageInputStream(file);
            return  ImageIO.read(is);
        }
    })

image = futureImage.get();

Pour enregistrer une image (Try-Catch-finalement omis):

Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
    @Override
    public Boolean call() {
        FileOutputStream os = new FileOutputStream(file); 
        return ImageIO.write(image, getFileFormat(), os);  
    }
});

boolean wasWritten = futureWrite.get();

Il est important de noter que vous devez vider et fermer vos flux dans un bloc finally. Je ne sais pas comment il fonctionne par rapport à d'autres solutions, mais il est assez polyvalent.

6
répondu ekangas 2017-05-23 12:26:23

Je vous suggère de regarder ThreadPoolExecutor newSingleThreadExecutor. Il gérera le maintien de vos tâches ordonnées pour vous, et si vous soumettez Callables à votre exécuteur, vous pourrez également obtenir le comportement de blocage que vous recherchez.

5
répondu codethulhu 2009-07-31 13:23:01

Vous pouvez essayer LinkedTransferQueue à partir de jsr166: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/

Il répond à vos exigences et a moins de frais généraux pour les opérations d'offre/sondage. Comme je peux le voir dans le code, lorsque la file d'attente n'est pas vide, elle utilise des opérations atomiques pour interroger les éléments. Et lorsque la file d'attente est vide, il tourne pendant un certain temps et garer le fil en cas d'échec. Je pense que cela peut aider dans votre cas.

4
répondu Vitaly 2009-08-01 06:48:12

J'utilise ArrayBlockingQueue chaque fois que j'ai besoin de passer des données d'un thread à un autre. En utilisant les méthodes put et take (qui bloqueront si plein / vide).

3
répondu Javamann 2009-07-31 17:12:22

Voici une liste de classes implémentant BlockingQueue.

je voudrais vous recommandons de vérifier SynchronousQueue.

Comme @ Rorick mentionné dans son commentaire, je crois que toutes ces implémentations sont simultanées. Je pense que vos préoccupations avec LinkedBlockingQueue peuvent être hors de propos.

2
répondu jjnguy 2009-07-31 13:11:48