103 Stimmen

Wie verwendet man ConcurrentLinkedQueue?

Wie verwende ich eine ConcurrentLinkedQueue in Java?
Mit diesem LinkedQueue Muss ich mir Sorgen um die Gleichzeitigkeit in der Warteschlange machen? Oder muss ich nur zwei Methoden definieren (eine zum Abrufen von Elementen aus der Liste und eine weitere zum Hinzufügen von Elementen zur Liste)?
Hinweis: Diese beiden Methoden müssen natürlich synchronisiert werden. Richtig?


EDITAR: Was ich versuche, ist Folgendes: Ich habe eine Klasse (in Java) mit einer Methode zum Abrufen von Elementen aus der Warteschlange und eine andere Klasse mit einer Methode zum Hinzufügen von Elementen zur Warteschlange. Die hinzugefügten und aus der Liste abgerufenen Elemente sind Objekte meiner eigenen Klasse.

Eine weitere Frage: Muss ich dies in der remove-Methode tun:

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

Ich habe nur einen Konsumenten und einen Produzenten.

0 Stimmen

Danke für die Antwort auf meine Frage. Was ich zu tun versuche, ist folgendes: Ich habe eine Klasse (in Java) mit einer Methode zum Abrufen von Elementen aus der Warteschlange und eine andere Klasse mit einer Methode zum Hinzufügen von Elementen zur Warteschlange. Die hinzugefügten und aus der Liste abgerufenen Elemente sind Objekte meiner eigenen Klasse.

2 Stimmen

Sie sollten Ihre Frage bearbeiten und diese Klarstellung in die Frage selbst aufnehmen.

165voto

Adam Jaskiewicz Punkte 10844

Nein, die Methoden müssen nicht synchronisiert werden, und Sie brauchen keine Methoden zu definieren; sie sind bereits in ConcurrentLinkedQueue enthalten, verwenden Sie sie einfach. ConcurrentLinkedQueue führt alle Sperren und andere Operationen, die Sie benötigen, intern aus; Ihr Producer (s) fügt Daten in die Warteschlange ein, und Ihre Konsumenten pollen für sie.

Erstellen Sie zunächst Ihre Warteschlange:

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

Nun, wo auch immer Sie Ihre Producer/Consumer-Objekte erstellen, übergeben Sie die Warteschlange, damit sie ihre Objekte irgendwo ablegen können (Sie könnten stattdessen einen Setter dafür verwenden, aber ich ziehe es vor, diese Art von Dingen in einem Konstruktor zu tun):

YourProducer producer = new YourProducer(queue);

und:

YourConsumer consumer = new YourConsumer(queue);

und fügen Sie in Ihrem Produzenten etwas hinzu:

queue.offer(myObject);

und nehmen Sie Dinge in Ihrem Consumer heraus (wenn die Warteschlange leer ist, gibt poll() null zurück, also überprüfen Sie es):

YourObject myObject = queue.poll();

Für weitere Informationen siehe die Javadoc

EDITAR:

Wenn Sie das Warten darauf, dass die Warteschlange nicht leer ist, blockieren müssen, sollten Sie wahrscheinlich eine LinkedBlockingQueue und verwenden Sie die Methode take(). LinkedBlockingQueue hat jedoch eine maximale Kapazität (Standardwert ist Integer.MAX_VALUE, was mehr als zwei Milliarden ist) und kann daher je nach den Umständen geeignet sein oder nicht.

Wenn Sie nur einen Thread haben, der etwas in die Warteschlange stellt, und einen anderen Thread, der etwas aus der Warteschlange nimmt, ist ConcurrentLinkedQueue wahrscheinlich übertrieben. Sie ist eher für den Fall gedacht, dass Hunderte oder gar Tausende von Threads gleichzeitig auf die Warteschlange zugreifen. Ihre Bedürfnisse werden wahrscheinlich durch die Verwendung erfüllt werden:

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

Ein Vorteil davon ist, dass es auf die Instanz (Warteschlange) sperrt, so dass Sie auf die Warteschlange synchronisieren können, um die Atomizität von zusammengesetzten Operationen zu gewährleisten (wie von Jared erklärt). Mit einer ConcurrentLinkedQueue können Sie dies NICHT tun, da alle Operationen OHNE Sperren auf der Instanz durchgeführt werden (unter Verwendung von java.util.concurrent.atomic-Variablen). Sie müssen dies NICHT tun, wenn Sie blockieren wollen, während die Warteschlange leer ist, da poll() einfach null zurückgibt, wenn die Warteschlange leer ist, und poll() ist atomar. Prüfen Sie, ob poll() null zurückgibt. Wenn ja, warten Sie() und versuchen Sie es dann erneut. Sperren ist nicht nötig.

Endlich:

Ehrlich gesagt, würde ich einfach eine LinkedBlockingQueue verwenden. Es ist immer noch Overkill für Ihre Anwendung, aber die Chancen sind es wird gut funktionieren. Wenn es nicht performant genug ist (PROFIL!), können Sie immer versuchen, etwas anderes, und es bedeutet, dass Sie nicht haben, um mit jeder synchronisierten Sachen zu beschäftigen:

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

Alles andere ist gleich geblieben. Setzen Sie wahrscheinlich wird nicht blockiert, weil Sie wahrscheinlich nicht zwei Milliarden Objekte in die Warteschlange stellen werden.

0 Stimmen

Vielen Dank für Ihre Antwort. Eine weitere Frage: muss ich dies in der remove-Methode tun: while (queue.size() == 0) wait(); queue.poll();

0 Stimmen

Ich werde diese Frage als Zusatz zu meiner Antwort beantworten, da sie ziemlich wichtig ist.

0 Stimmen

Das hat in einer anderen Frage für Verwirrung gesorgt, Collection.synchronizedList gibt eine List die nicht implementiert Queue .

39voto

Jared Punkte 24266

Dies ist weitgehend eine Duplikat einer anderen Frage .

Hier ist der Abschnitt der Antwort, der für diese Frage relevant ist:

Muss ich meine eigene Synchronisierung durchführen, wenn ich java.util.ConcurrentLinkedQueue verwende?

Atomare Operationen auf die gleichzeitigen Sammlungen werden für Sie synchronisiert. Mit anderen Worten: Jeder einzelne Aufruf der Warteschlange ist garantiert thread-sicher, ohne dass Sie etwas dafür tun müssen. Was ist no garantiert thread-safe sind alle Operationen, die Sie mit der Sammlung durchführen und die nicht atomar sind.

Dies ist zum Beispiel thread-sicher, ohne dass Sie etwas tun müssen:

queue.add(obj);

oder

queue.poll(obj);

Nicht-atomare Aufrufe der Warteschlange sind jedoch nicht automatisch thread-sicher. Zum Beispiel sind die folgenden Operationen no automatisch thread-sicher:

if(!queue.isEmpty()) {
   queue.poll(obj);
}

Letzteres ist nicht thread-sicher, da es sehr gut möglich ist, dass zwischen dem Aufruf von isEmpty und dem Aufruf von poll andere Threads Elemente der Warteschlange hinzugefügt oder entfernt haben. Der thread-sichere Weg, dies zu tun, ist wie folgt:

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

Nochmals: atomare Aufrufe der Warteschlange sind automatisch thread-sicher. Nicht-atomare Aufrufe sind es nicht.

1 Stimmen

Die einzige übliche Anwendung, die mir einfällt, ist das Blockieren, bis die Warteschlange nicht mehr leer ist. Wäre das nicht besser durch die Verwendung einer Implementierung von BlockingQueue (take() ist atomar und blockiert, bis es etwas zu verbrauchen ist) serviert werden?

6 Stimmen

Auch damit muss man vorsichtig sein. Im Gegensatz zu synchronizedList, ConcurrentLinkedQueue synchronisiert nicht auf sich selbst, so dass in Ihrem Code wäre es immer noch möglich, für einen Produzenten, um die Warteschlange zu bieten, während Sie in Ihrem synchronisierten Block sind.

0 Stimmen

Warum sollten Sie queue.isEmpty() und queue.poll() kombinieren? Kann man nicht einfach abfragen und prüfen, ob das Ergebnis null ist? (Meines Wissens nach gibt eine leere Warteschlange null zurück, wenn man sie abfragt)

8voto

marq Punkte 768

Dies ist wahrscheinlich das, was Sie in Bezug auf die Threadsicherheit und "Schönheit" suchen, wenn Sie versuchen, alles in der Warteschlange zu verbrauchen:

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

Dadurch wird gewährleistet, dass Sie die Warteschlange beenden, wenn sie leer ist, und dass Sie weiterhin Objekte aus der Warteschlange entfernen, solange sie nicht leer ist.

1 Stimmen

Sehr nützlich, um eine Warteschlange zu leeren. Danke.

6voto

Hank Gay Punkte 67607

Utilisez [Umfrage](http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html#poll()) um das erste Element zu erhalten, und ajouter um ein neues letztes Element hinzuzufügen. Das war's, keine Synchronisation oder sonstiges.

2voto

siddhadev Punkte 16181

Die ConcurentLinkedQueue ist eine sehr effiziente wait/lock free-Implementierung (siehe javadoc für die Referenz), so dass Sie nicht nur nicht synchronisieren müssen, aber die Warteschlange wird nicht sperren nichts, so dass praktisch so schnell wie eine nicht synchronisierte (nicht thread safe) ein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X