Użycie kolekcji thread-safe

0

Zaczynam przygodę z programowaniem wielowątkowym, do tego czasu miałem bardzo mało do czynienia z tym. Proszę o wyrozumiałość :D

Pierwsze pytanie jakie mam to jeżeli używam jakiejś kolekcji thread-safe np. ConcurrentLinkedQueue i mam sytuację taką, że jest pusta to na wywołuję kolejka.wait(). No i czekamy. W momencie gdy coś jest wrzucane do kolejki chcę powiadomić inne wątki więc wołam kolejka.notifyAll() no ale nie mogę tego wywołać gdy nie jestem w bloku synchronized. Czy to poprawne, żeby otwierać blok synchronized tylko po to, żeby wywołać notifyAll? Bo coś mi się nie wydaje... Jak to zrobić poprawnie? Bo rozumiałbym to tak, że jeżeli używam kolekcji thread-safe to nie muszę się już zbytnio zajmować synchronizacją tej kolejki bo synchronizacja powinna być robiona gdzieś "pod spodem".

0

Bo rozumiałbym to tak, że jeżeli używam kolekcji thread-safe to nie muszę się już zbytnio zajmować synchronizacją tej kolejki bo synchronizacja powinna być robiona gdzieś "pod spodem".

Tak, masz rację. To co robisz nie jest potrzebne.

0
Jaca777 napisał(a):

Bo rozumiałbym to tak, że jeżeli używam kolekcji thread-safe to nie muszę się już zbytnio zajmować synchronizacją tej kolejki bo synchronizacja powinna być robiona gdzieś "pod spodem".

Tak, masz rację. To co robisz nie jest potrzebne.

No OK ale jak mam poinformować wątki które czekają gdy kolejka jest pusta, że coś się zmieniło? Bo jak nie robię notifyAll tylko samo wywołanie add do kolejki to po prostu nie startują...

0

Do takiego użycia są raczej przeznaczone implementacje BlockingQueue.

0
zz napisał(a):

Do takiego użycia są raczej przeznaczone implementacje BlockingQueue.

Faktycznie zgodnie z dokumentacją to by się zgadzało ale nadal mam problem z tym wybudzaniem wątków...

0

Ale po co wybudzać? W dokumentacji https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html masz tabelkę z blokującymi metodami. Budzenie wątków jest zaimplementowane w samej kolejce.

0

Ale zrobiłem coś takiego, że osobny wątek sobie zagląda do tej kolejki i jeśli tam coś jest to przetwarza a jeśli nie to powinien czekać a nie zużywać procesora. I teraz tak, jeśli nie mam synchronized to nie mogę mu dać wait jeśli wstawie blok synchronized, uśpię go to znowu nie mam go jak obudzić... i nie bardzo wiem jak to teraz zrobić dobrze.

0

Ale po co te wait i synchronized? Wywołanie take blokuje.

0

Najpewniej czegoś tu jeszcze nie rozumiem :( jak wyżej chodzi mi o to, że inny wątek dodaje do kolejki a inny przetwarza jej elementy wiec jak jest pusta to niech śpi a jak się to zmieni to muszę go jakoś poinformować o tym...

0

Kolejka sama to robi.

0

No to inaczej. Mam sobie klasę i w niej mam tą kolejkę. Tworzę sobie nowy wątek który ma procesować obiekty w kolejce gdy są, gdy ich nie ma czekać i ewentualnie się obudzić jeśli się pojawią więc metoda run() tego wątku wygląda tak:

@Override
public void run() {

while (true) {

 synchronized (queue) {

     while (queue.isEmpty()) {
         try {
              queue.wait();
             } catch (Exception ex) {
                       Logger.getLogger(Queues.class.getName()).log(Level.SEVERE, null, ex);
             }
            }

}
//jakieś operacje na obiektach kolejki zdejmując obiekty
while (!queueToSend.isEmpty()) {

try {
       process(queue.poll());

       } catch (InterruptedException ex) {
           Logger.getLogger(SwitchSimulator.class.getName()).log(Level.SEVERE, null, ex);
      }
   }
}

Powiedzmy, że wątek opróżnił kolejkę wiec poszedł potem spać, żeby nie obciążać procesora.
Mam teraz metodę która dodaje obiekty do kolejki za pomocą queue.put.
I teraz jeśli nie wykonam po tym

synchronized (queue) {
 queue.notifyAll();
}

Wątek się nie budzi i nie kontynuuje pracy. Co robię źle?

0

Nie powinieneś używać ani wait, ani notify.

ma procesować obiekty w kolejce gdy są, gdy ich nie ma czekać i ewentualnie się obudzić jeśli się pojawią

Metoda take 'usypia' wątek, jeśli kolejka jest pusta, i budzi go, gdy coś się w niej pojawi. Nie musisz robić nic, prócz prostej pętli z wywołaniem take.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html#take--

0
Jaca777 napisał(a):

Nie powinieneś używać ani wait, ani notify.

ma procesować obiekty w kolejce gdy są, gdy ich nie ma czekać i ewentualnie się obudzić jeśli się pojawią

Metoda take 'usypia' wątek (i budzi go, gdy coś się w niej pojawi), jeśli kolejka jest pusta. Nie musisz robić nic, prócz prostej pętli z wywołaniem take.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html#take--

Tylko, że moja pętla w wątku while(true) powoduje, że wtedy wątek nie idzie spać i cały czas mam obciążenie procesora nawet jak kolejka jest już opróżniona.

0

Wywołujesz w tej pętli metodę take z BlockingQueue, tak? Pokażesz kod?

1

@Hepek czytanie ze zrozumieniem lvl -1. Blokujące kolekcje blokują sie na niektórych metodach i koniec. Coś takiego:

Queue<Integer> q = new BlockingQueue<>();
while(true){
    q.take();
}

będzie zabierało 0% procesora bo wątek po prostu się zatrzyma oczekując na dane w kolejce.

0

@Jaca777 kod pokazałem wyżej @Shalom jak napisałem to są moje początki z programowaniem wielowątkowym więc nie jestem wszystko wiedzący...
Dzięki póki co

0

A w przypadku gdy wiele wątków może dodawać elementy do kolejki rozumiem, że powinienem użyć ConcurrentLinkedQueue?

0

Wszystkie implementacje BlockingQueue są thread-safe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1