Wybudzenie konkretnego wątku

0

Witam. Właśnie zaczynam pisać pewien program wykorzystujący wątki. Na planszy mam pudełko oraz latające kulki. Każde z nich to oddzielny wątek (pudełko oraz kulki mają własne klasy). Gdy kulka natknie się na ściankę pudełka, przykleja się do niej. Do wnętrza pudełka wpuszczana jest jedna kulka (pudełko jest wybudzane poprzez notify()). Wewnątrz pudełka może w danej chwili być tylko jedna kulka (pudełko w tym czasie ma wywołaną metodę wait()). Gdy kulka opuszcza pudełko, wpuszczona zostaje następna.
Niestety, nie wiem jak wysłać notify() do wątku obsługującego pudełko (na pewno realizowałaby to kulka wychodząca). Jeśli czeka jedna kulka to nie ma problemu, ale co jeśli jest ich kilka? Notify() działa losowo, więc najpewniej "trafi" inną kulkę. Prosiłbym o jakąś wskazówkę, jak rozwiązać ten problem.

0

Ja pamiętam 2 zasady:

  1. nie używaj wait i notify
  2. jeśli już używasz, to zapomnij o notify i używaj tylko notifyAll
0

Przyznam szczerze, że nie wiem skąd te zasady. Zgaduję, że chodzi o to, by wątek w międzyczasie mógł robić coś pożyteczniejszego niż bezczynne oczekiwanie.
Problem w tym, że zgodnie z treścią zadania, mam użyć właśnie wait() i notify(). Na liście metod, z którymi mam się zapoznać są natomiast: run(), start(), sleep(), interrupt(), suspend(), resume().

EDIT: Przeczytałem kilkukrotnie treść zadania. Nie wiem jakim cudem, ale pomieszały mi się warianty (jeden do wyboru). Przedstawiony przeze mnie wariant nie wymaga, by pudełko było wątkiem.
To moim zdaniem znacząco upraszcza sprawę. Natomiast wariant, który faktycznie wymaga tego dodatkowego wątku sprowadza się do rozwiązania problemu śpiącego golibrody.

0

Czy kulka agreguje (ma w sobie) wątek pudełka?
this(kulka).wątekPudelka.notify() ?

... w sumie nie wiem ;p

0

Nie. Zarówno kulka, jak i pudełko to oddzielne wątki. Oczywiście, jeśli rozpatrujemy wyżej wspomniany wariant z problemem śpiącego golibrody. W przeciwnym wypadku pudełko nie posiada swojego wątku, a jest tylko obiektem.

0

@Delor: Niezbyt dobrze rozumiem ideę kolejki blokującej. Czy chodzi o to, że wątek, który "wrzuci" kulkę do kolejki zostanie zablokowany do momentu wywołania metody ArrayBlockingQueue.take()?

0

Kolejkę tworzy się podaną wielkością.
Konsument pobiera z kolejki (take()). Jeżeli kolejka jest pusta to jest blokowany i czeka na element.
Producent wkłada do kolejki (put()) i jeżeli w kolejce skończy się miejsce to jest blokowany i czeka na zwolnienie miejsca.
Pary metod offer()/poll() i add()/remove() to dodawanie/wyjmowanie nieblokujące (zwracają bool lub rzucają wyjątek gdy nie mogą niezwłocznie wykonać operacji).
Metody offer()/poll() z timeoutami to wersje blokujące na pewien czas.

0

Czyli wątek, pomimo dodania czegoś do kolejki, wciąż się wykonuje.
Mam pewien pomysł. Pudełko co pewien czas mogłoby wydawać "bileciki", które byłyby odbierane przez kulki oczekujące na wejście.

EDIT: Wiem, że to dość głupie pytanie, ale czy możliwe jest, by w jednym "przejściu" kolejki ten sam wątek dwukrotnie dodał coś do niej?

0

Bilety to dobry pomysł.
Wątek może dodawać do kolejki ile zechce (do skończenia miejsca w kolejce).

0

Zastanawia mnie jedno: po co pudełko ma być wątkiem? Z treści zadania wynika, że wątkami mają być kulki, one się poruszają, przyklejają się do pudełka, wchodzą i wychodzą. Pudełko to zasób, który tylko ma synchronizować i koordynować kulki.
Pierwsze rozwiązanie to zrobić pudełko jako monitor, z dwoma metodami: enter() i exit(), obie synchronizowane. Obiekt pudełka ma licznik, ile kulek jest w środku. W enter(), najpierw testujesz, czy w pudełku jest dozwolona liczba kulek, zwiększasz licznik kulek i koniec, Jeśli brak miejsca na kulkę, to robisz wait() na obiekcie pudełka. W exit() wywołujesz notifyAll() jeśli liczba kulek w pudełku jest równa maksimum i zmniejszasz licznik kulek. W wątku kulek, przed wykonaniem ruchu, oceniasz gdzie znajdzie się kulka jeśli była na zewnątrz pudełka i ma wejść do pudełka to wywołuje enter(), jeśli była w środku i wychodzi to exit(), pozostałe warunki to sekcja lokalna.
Drugie rozwiązanie to wykorzystanie wspomnianej ArrayBlockingQueue, która będzie symulować swoim put() funkcję enter() a take() exit(), a długość kolejki liczbę dozwolonych kulek w pudełku.

0

Dzięki za podpowiedź. Pudełko ma być wątkiem, ponieważ tak to zostało przedstawione w treści zadania.

0

A co ten wątek pudełka ma robić? Jest to podane w zadaniu? W pierwszym poście napisałeś, że kulki i pudełko mają swoje klasy, co nie oznacza, że muszą mieć swoje wątki. Może pokaż oryginał tego zadania.

0

Wybacz, że długo nie odpisywałem. Jedyne, co wątek pudełka ma robić, to co określony czas wpuszczać do środka kolejne piłki.

Praktycznie już kończę mój program, jednak pojawił się pewien problem. Program działa w pełni prawidłowo do momentu napotkania którejś z kulek na pudełko. Wtedy zatrzymuje się wszystko. Co może być nie tak? Wolałbym nie ujawniać całego kodu, stąd podaję tylko jego część.

Metoda move():

synchronized void move()
    {
        if(position_x-ballRadius<=usingMinLimit_x || position_x+ballRadius>=usingMaxLimit_x)
        {
            dx*=-1;
        }

        if(position_y-ballRadius<=usingMinLimit_y || position_y+ballRadius>=usingMaxLimit_y)
        {
            dy*=-1;
        }

        if(position_x+ballRadius>=box.getOuterSquareMinX() && position_x-ballRadius<=box.getOuterSquareMaxX() &&
                position_y+ballRadius>=box.getOuterSquareMinY() && position_y-ballRadius<=box.getOuterSquareMaxY())
        {
            try{
                //timeline.pause();
                //wait();
                myTicket=queue.take();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        position_x += dx*speed;
        position_y += dy*speed;

        ball.setCenterX(position_x);
        ball.setCenterY(position_y);

    }

Metoda run():

@Override
    public void run()
    {
        timeline = new Timeline(new KeyFrame(Duration.millis(duration), actionEvent -> move()));
        timeline.setCycleCount(Animation.INDEFINITE);

        timeline.play();
    }
0
synchronized void move()

Wątek zakłada blokadę na obiekt kuli po czym

myTicket=queue.take();

czeka na bilet.
Gdy w tym czasie inny wątek (np. wątek rysujący kule lub wątki innych kul sprawdzające odległość od tej kuli) wywoła inną synchronizowaną metodę tego obiektu to będzie musiał poczekać razem z nią.

0

Problem w tym, że nie tworzyłem niczego oddzielnego do rysowania kuli. Wszystkie metody są w ramach jednej klasy Ball . Nigdzie też nie sprawdzam odległości od pozostałych kul.

Nie mam zielonego pojęcia, co się dzieje. Po wywołaniu take() lub wait() wątek powinien się zatrzymać i oddawać "swoją kolej" innym wątkom aż do wywołania put() lub notify()/notifyAll() przez inny wątek. A w moim przypadku cały program przestaje odpowiadać...
W tej chwili całe wywołanie take() wrzuciłem do oddzielnej synchronizowanej funkcji i wstawiłem jej wywołanie do move() (z którego usunąłem synchronized). Wciąż to samo...

0

Co z timeline?

A running Timeline is being referenced from the FX runtime.

0

W moim przypadku zatrzymywany jest także timeline. Czy może chodzić o to, że timeline'y z pozostałych wątków także się zatrzymują? Jeśli tak, to czy można to jakoś obejść?

EDIT: Już zaczynam rozumieć, o co chodzi. Znasz może jakiś sposób, by móc skorzystać z metod wait()/notify() lub ArrayBlockingQueue.put()/take()?

0

Niech kulka sama się porusza a timeline tylko odczytuje jej współrzędne. Blokadę załóż na dostęp do współrzędnych a nie na cały obiekt kuli.

0

Przyznam, że nie za bardzo wiem, co masz na myśli. Czy dobrze rozumiem, że do aktualizacji współrzędnych będę potrzebował innej klasy/wątku?

0

Zrób to w wątku kuli. Po wywołaniu timeline.play().

0

A gdzie jest queue.put()?Jeśli tylko wyciągasz z kolejki, a ona jest pusta to cała metoda move() jest wstrzymywana do czasu, aż ktoś zrobi put(). Synchronizowanie w tym przypadku całej metody move() jest albo szkodliwe albo zbędne. Nie jestem wróżem Maciejem, więc jak napisał @Delor: jeśli na tym samym obiekcie wywołasz to move() w dwu lub więcej wątkach to tylko jeden wątek może ją wykonać, a pozostałe będą zablokowane do czasu aż ten wybraniec zakończy move(). Jeśli ten jedyny wątek utknie na queue.take() to praktycznie masz zakleszczenie. Jeśli put wykonuje wątek pudełka to analizuj, czy tam coś nie bangla.
Jeśli move() to metoda kulek i jest wywoływana tylko w wątku kulki, to synchronized jest niepotrzebne.
Nic nie pisałeś, że pudełko ma wpuszczać do środka kulki co pewien czas, to zmienia podejście, tak i to, co robią kulki w pudełku, kiedy wychodzą z pudełka itd.
Gdyby zadanie polegało na tym, że kulki latają sobie jak chcą, gdy napotkają pudełko to się przyklejają i czekają na wpuszczenie, a pudełko wpuszcza maksymalnie 1 kulkę, która w pudełku też lata i sama "decyduje" kiedy opuścić pudełko, to można by to rozwiązać tak

0

Póki co umieściłem tylko queue.take() i odpaliłem program, żeby zobaczyć, czy wszystko, do momentu napotkania na pudełko, działa prawidłowo. Move już nie posiada synchronized. Zamiast tego jest oddzielna metoda synchronized void takeTicket(), która jest wywoływana w miejsce queue.take() w metodziemove().

Zgodnie z radą @Delor zacząłem pracować nad rozdzieleniem Timeline'a od zmiany współrzędnych. Są pewne postępy, lecz wciąż szukam sposobu, by współrzędne zmieniały się co jedną klatkę.
Póki co za timeline.play() wrzuciłem zwykłą pętlę wywołującą move(). Program już się nie zatrzymuje, lecz zgodnie z przewidywaniami kulka rysuje się od razu przyklejona.

EDIT: Niestety, wciąż nie wiem, jak wybrnąć z powyższej sytuacji. Czy zna ktoś jakiś sposób, by tylko co każdą klatkę zmieniać współrzędne? Wiem, że zmiana musi następować w klasie Ball, całkowicie poza EventHandler.

EDIT2: Problem rozwiązany. EventHandler zmienia współrzędne, ale poza nim mam dodatkową pętlę, która sprawdza, czy kulka nie zetknęła się z pudełkiem. Gdy to nastąpi, timeline jest pauzowany, zmienne dx oraz dy zmieniane są na zero i wywoływane jest timeline.play().

Dziękuję wszystkim za pomoc i cenne rady :)
Pozdrawiam.

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