2009-05-06 7 views
22

BlockingQueue a la méthode appelée drainTo() mais n'est pas bloquée. J'ai besoin d'une file d'attente que je veux bloquer mais aussi capable de récupérer des objets en file d'attente dans une seule méthode. Je suppose que le code ci-dessus va fonctionner, mais je suis à la recherche d'une solution élégante.BlockingQueue - méthodes drainTo() bloquées

Répondre

30

Vous parlez de commentaire dans le JavaDoc:

De plus, le comportement de cette opération est définie si la collection spécifiée est modifiée pendant que l'opération est en cours.

Je crois que cela fait référence à la collection list dans votre exemple:

blockingQueue.drainTo(list); 

qui signifie que vous ne pouvez pas modifier list en même temps que vous drainent de blockingQueue en list. Cependant, la file d'attente de blocage se synchronise de manière interne de sorte que lorsque drainTo est appelée, puts et (voir la remarque ci-dessous) seront bloqués. Si ce n'était pas le cas, cela ne serait pas vraiment sans danger pour les threads. Vous pouvez regarder le code source et vérifier que drainTo est thread-safe concernant la file d'attente de blocage elle-même.

Alternativement, voulez-vous dire que lorsque vous appelez drainTo que vous voulez qu'il bloque jusqu'à ce qu'au moins un objet a été ajouté à la file d'attente? Dans ce cas, vous avez peu d'autre choix que:

list.add(blockingQueue.take()); 
blockingQueue.drainTo(list); 

pour bloquer jusqu'à ce qu'un ou plusieurs éléments ont été ajoutés, puis égoutter toute la file d'attente dans la collection list.

Remarque: Depuis Java 7, un verrou séparé est utilisé pour les transactions get et puts. Les opérations de Put sont maintenant permises pendant un drainTo (et un certain nombre d'autres opérations de prise).

+0

Il est peu probable que la collection à laquelle vous copiez soit thread-safe. Quel serait le but? –

+0

Est-il garanti que drainTo (list) n'appellera pas list.clear()? Je ne vois pas ça dans le javadoc n'importe où. – Recurse

+0

@Recurse: Le JavaDoc ne * garantit * pas que drainTo (list) n'appellera pas clear(), je suppose, car il ne dit pas qu'il ne le fera pas. Cependant, il serait très surprenant que l'on fasse quelque chose de si important à une collection sans documenter cette action. Beaucoup considéreraient ceci comme un bug sérieux. – Eddie

0

Avec l'API disponible, je ne pense pas que vous allez devenir beaucoup plus élégant. Autre que vous pouvez supprimer le test de taille.

Si vous souhaitez extraire de manière atomique une séquence contiguë d'éléments même si une autre opération de suppression coïncide, je ne crois pas que drainTo le garantisse.

+2

drainPermet définitivement de bloquer tout put, get, take, etc ... Vérifiez le code source. Cette API est thread-safe contre d'autres appels à la file d'attente de blocage. Cependant, si la collection à laquelle vous vidange change pendant drainTo, vous pouvez avoir un problème. – Eddie

+2

Vérifiez le code source? C'est une interface! –

+2

OK, vrai, drôle, mais à part le point. Vérifiez les classes d'implémentation dans le JDK. (Ce qui est clairement ce que je voulais dire.) – Eddie

0

code source:

596:  public int drainTo(Collection<? super E> c) { 
       //arg. check 
603:   lock.lock(); 
604:   try { 
608:    for (n = 0 ; n != count ; n++) { 
609:     c.add(items[n]); 
613:    } 
614:    if (n > 0) { 
618:     notFull.signalAll(); 
619:    } 
620:    return n; 
621:   } finally { 
622:    lock.unlock(); 
623:   } 
624:  } 

ArrayBlockingQueue est impatient de retourner 0. BTW, il pourrait le faire avant de prendre le verrou.

+1

Non. Prendre le verrou ne permet pas seulement une exclusion mutuelle, il s'assure également que le compte vu est "frais", que les mises à jour de la mémoire principale provenant d'autres threads sont visibles. Vous pouvez rendre le compte "volatile", mais les écritures volatiles sont relativement lentes, donc l'écriture signifierait à la fois le verrouillage et l'écriture volatile, probablement plus de frais généraux. À mon humble avis tout développeur développeur code concurrent devrait tenter de faire de son mieux pour comprendre le modèle de mémoire de Java: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 –

5

J'ai trouvé ce modèle utile.

List<byte[]> blobs = new ArrayList<byte[]>(); 
if (queue.drainTo(blobs, batch) == 0) { 
    blobs.add(queue.take()); 
} 
10

Si vous arrive d'utiliser Google goyave, il y a une méthode astucieuse Queues.drain().

Drains la file d'attente comme BlockingQueue.drainTo(Collection, int), mais si les demandées numElements éléments ne sont pas disponibles, il attendra les jusqu'à le délai spécifié.

+0

C'est exactement ce dont j'avais besoin, merci. – Alan

Questions connexes