Jak korzystać z ConcurrentLinkedQueue?

96

Jak używać ConcurrentLinkedQueuew Javie?
Używając tego LinkedQueue, czy muszę się martwić o współbieżność w kolejce? Czy muszę po prostu zdefiniować dwie metody (jedną do pobierania elementów z listy, a drugą do dodawania elementów do listy)?
Uwaga: oczywiście te dwie metody muszą być zsynchronizowane. Dobrze?


EDYCJA: To, co próbuję zrobić, to to: mam klasę (w Javie) z jedną metodą pobierania elementów z kolejki i inną klasą z jedną metodą dodawania elementów do kolejki. Elementy dodane i pobrane z listy są obiektami mojej własnej klasy.

Jeszcze jedno pytanie: czy muszę to zrobić w metodzie usuwania:

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

Mam tylko jednego konsumenta i jednego producenta.

Ricardo Felgueiras
źródło
Dzięki za odpowiedź na pytanie mt. Oto co próbuję zrobić: mam klasę (w Javie) z jedną metodą pobierania elementów z kolejki i inną klasą z jedną metodą dodawania elementów do kolejki. Elementy dodane i pobrane z listy są obiektami mojej własnej klasy.
Ricardo Felgueiras,
2
Powinieneś edytować swoje pytanie i umieścić to wyjaśnienie w samym pytaniu.
Adam Jaskiewicz

Odpowiedzi:

157

Nie, metody nie muszą być synchronizowane i nie musisz definiować żadnych metod; są już w ConcurrentLinkedQueue, po prostu ich używaj. ConcurrentLinkedQueue wykonuje wewnętrznie wszystkie operacje blokowania i inne potrzebne; Twoi producenci dodają dane do kolejki, a Twoi konsumenci sondują je.

Najpierw utwórz kolejkę:

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

Teraz, gdziekolwiek tworzysz swoje obiekty producenta / konsumenta, przekaż kolejkę, aby mieli gdzie umieścić swoje obiekty (zamiast tego możesz użyć do tego ustawiacza, ale ja wolę zrobić to w konstruktorze):

YourProducer producer = new YourProducer(queue);

i:

YourConsumer consumer = new YourConsumer(queue);

i dodaj do tego rzeczy w swoim producencie:

queue.offer(myObject);

i wyjmij rzeczy z twojego konsumenta (jeśli kolejka jest pusta, poll () zwróci null, więc sprawdź to):

YourObject myObject = queue.poll();

Aby uzyskać więcej informacji, zobacz Javadoc

EDYTOWAĆ:

Jeśli chcesz zablokować oczekiwanie, aż kolejka nie będzie pusta, prawdopodobnie chcesz użyć LinkedBlockingQueue i metody take (). Jednak LinkedBlockingQueue ma maksymalną pojemność (domyślnie Integer.MAX_VALUE, czyli ponad dwa miliardy), a zatem może być odpowiedni lub nie w zależności od okoliczności.

Jeśli masz tylko jeden wątek, który umieszcza rzeczy w kolejce, a inny wątek pobiera rzeczy z kolejki, ConcurrentLinkedQueue jest prawdopodobnie przesadą. Jest to bardziej przydatne, gdy możesz mieć setki lub nawet tysiące wątków uzyskujących dostęp do kolejki w tym samym czasie. Twoje potrzeby prawdopodobnie zostaną zaspokojone poprzez zastosowanie:

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

Zaletą tego jest to, że blokuje się na instancji (kolejce), dzięki czemu można synchronizować w kolejce, aby zapewnić atomowość operacji złożonych (jak wyjaśnił Jared). NIE MOŻESZ tego zrobić z ConcurrentLinkedQueue, ponieważ wszystkie operacje są wykonywane BEZ blokowania instancji (przy użyciu zmiennych java.util.concurrent.atomic). NIE będziesz musiał tego robić, jeśli chcesz blokować, gdy kolejka jest pusta, ponieważ funkcja poll () zwróci wartość null, gdy kolejka jest pusta, a funkcja poll () jest atomowa. Sprawdź, czy poll () zwraca null. Jeśli tak, poczekaj () i spróbuj ponownie. Nie ma potrzeby blokowania.

Wreszcie:

Szczerze, po prostu użyłbym LinkedBlockingQueue. Nadal jest to przesada dla twojej aplikacji, ale są szanse, że będzie działać dobrze. Jeśli nie jest wystarczająco wydajny (PROFIL!), Zawsze możesz spróbować czegoś innego, a to oznacza, że ​​nie musisz zajmować się ŻADNYMI zsynchronizowanymi rzeczami:

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.

Cała reszta jest taka sama. Opcja Put prawdopodobnie nie zostanie zablokowana, ponieważ prawdopodobnie nie umieścisz dwóch miliardów obiektów w kolejce.

Adam Jaskiewicz
źródło
Dzięki za twoją odpowiedź. Jeszcze jedno pytanie: czy muszę to zrobić w metodzie usuwania: while (queue.size () == 0) wait (); queue.poll ();
Ricardo Felgueiras,
Odpowiem na to jako poprawka do mojej odpowiedzi, ponieważ jest to bardzo ważne.
Adam Jaskiewicz
Ponieważ spowodowało zamieszanie w innym pytaniu, Collection.synchronizedListzwraca a, Listktóre nie implementuje Queue.
Tom Hawtin - tackline
@AdamJaskiewicz używa ConcurrentLinkedQueuedla Producer Consumer dobrego pomysłu, odnoszę się do tego posta stackoverflow.com/questions/1426754/ ...
rd22
37

W dużej mierze jest to duplikat innego pytania .

Oto sekcja tej odpowiedzi, która ma znaczenie dla tego pytania:

Czy muszę wykonywać własną synchronizację, jeśli używam java.util.ConcurrentLinkedQueue?

Operacje atomowe na współbieżnych kolekcjach są synchronizowane za Ciebie. Innymi słowy, każde indywidualne wywołanie kolejki jest gwarantowane jako bezpieczne wątkowo bez żadnej akcji z Twojej strony. Co jest nie gwarantuje bezpieczny wątku są wszelkie operacje wykonać na zbiorach, które są non-atomowych.

Na przykład jest to ochrona wątków bez żadnej akcji z Twojej strony:

queue.add(obj);

lub

queue.poll(obj);

Jednak; Nieatomowe wywołania kolejki nie są automatycznie bezpieczne dla wątków. Na przykład następujące operacje nie są automatycznie chronione wątkami:

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

Ten ostatni nie jest bezpieczny dla wątków, ponieważ jest bardzo możliwe, że między wywołaniem isEmpty a wywołaniem sondażu inne wątki będą miały dodawane lub usuwane elementy z kolejki. Bezpieczny wątek sposób na to wygląda następująco:

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

Ponownie ... niepodzielne wywołania kolejki są automatycznie bezpieczne dla wątków. Wezwania nieatomowe nie są.

Jared
źródło
1
Jedynym typowym zastosowaniem, jakie mogę mieć, jest blokowanie, dopóki kolejka nie będzie pusta. Czy nie byłoby lepiej, gdyby zastosowano implementację BlockingQueue (metoda take () jest atomowa i blokuje, dopóki nie będzie czegoś do skonsumowania)?
Adam Jaskiewicz
6
Z tym też musisz uważać. W przeciwieństwie do synchronizedList, ConcurrentLinkedQueue nie synchronizuje się ze sobą, więc w Twoim kodzie nadal byłoby możliwe, aby producent oferował do kolejki, gdy jesteś w zsynchronizowanym bloku.
Adam Jaskiewicz
Dlaczego miałbyś połączyć queue.isEmpty () i queue.poll ()? Nie możesz po prostu sondować i sprawdzić, czy wynik jest pusty? (Rozumiem, że jeśli
sondujesz
Zgadzam się ze wszystkim w twoim kodzie z wyjątkiem ostatniego bloku kodu. Synchronizacja w ConcurrentLInkedQueue nie gwarantuje niczego, ponieważ inne wywołania nie są synchronizowane. Więc może się zdarzyć "add (..)" z innego wątku między twoją "isEmpty ()" i "poll (...)", mimo że zsynchronizowałeś ten wątek
Klitos G.
6

Użyj ankiety, aby uzyskać pierwszy element, i dodaj, aby dodać nowy ostatni element. To wszystko, bez synchronizacji ani nic innego.

Hank Gay
źródło
6

Prawdopodobnie właśnie tego szukasz pod względem bezpieczeństwa wątków i „ładności”, gdy próbujesz skonsumować wszystko w kolejce:

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

Gwarantuje to, że wyjdziesz, gdy kolejka jest pusta, i będziesz nadal usuwać z niej obiekty, o ile nie jest pusta.

marq
źródło
Bardzo przydatne do opróżniania kolejki. Dzięki.
DevilCode
2

ConcurentLinkedQueue to bardzo wydajna implementacja bez czekania / blokowania (zobacz javadoc w celach informacyjnych), więc nie tylko nie musisz synchronizować, ale kolejka niczego nie blokuje, dzięki czemu jest praktycznie tak szybka jak niezsynchronizowana bezpieczny) jeden.

siddhadev
źródło
1

Po prostu używaj go tak, jak kolekcji niewspółbieżnej. Klasy Concurrent [Collection] opakowują zwykłe kolekcje, dzięki czemu nie trzeba myśleć o synchronizacji dostępu.

Edycja: ConcurrentLinkedList nie jest w rzeczywistości tylko opakowaniem, ale raczej lepszą współbieżną implementacją. Tak czy inaczej, nie musisz się martwić o synchronizację.

Ben S.
źródło
ConcurrentLinkedQueue nie jest. Jest specjalnie zbudowany od podstaw z myślą o jednoczesnym dostępie wielu producentów i konsumentów. Nieco bardziej wyszukane niż proste opakowania zwrócone przez Kolekcje.
Zsynchronizowane
Tak, w J2SE 5 dodano wiele
fajnych