• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Java - wielowątkowość - kolejność

Object Storage Arubacloud
+2 głosów
537 wizyt
pytanie zadane 2 lipca 2018 w Java przez Tomasz Rogalski Bywalec (2,800 p.)
class Oczekujacy implements Runnable {

    private final Object blokada;
    private final Object blokada2;

    public Oczekujacy(Object blokada, Object blokada2) {
        this.blokada = blokada;
        this.blokada2 = blokada2;
    }

    @Override
    public void run() {
        synchronized (blokada) {
            System.out.println(Thread.currentThread().getName() + ": Wejście do sekcji synchronizowanej");
            synchronized (blokada2) {
                blokada2.notifyAll();
            }
            try {
                blokada.wait();
                System.out.println(Thread.currentThread().getName() + ": Oczekiwanie");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": Wykonanie sekcji krytycznej");
        }
    }
}

class Dostarczajacy implements Runnable {

    private Object blokada;

    public Dostarczajacy(Object blokada) {
        this.blokada = blokada;
    }

    @Override
    public void run() {
        synchronized (blokada) {
            System.out.println("Wybudzam wszytskie wątki");
            blokada.notifyAll();
        }
    }
}

class Testowa {

    public static void main(String[] args) throws InterruptedException {
        Object blokada = new Object();
        Object blokada2 = new Object();
        synchronized (blokada2) {
            Oczekujacy oczekujacy_1 = new Oczekujacy(blokada, blokada2);
            new Thread(oczekujacy_1, "1").start();
            blokada2.wait();

            Oczekujacy oczekujacy_2 = new Oczekujacy(blokada, blokada2);
            new Thread(oczekujacy_2, "2").start();
            blokada2.wait();

            Dostarczajacy dostarczajacy = new Dostarczajacy(blokada);
            new Thread(dostarczajacy).start();
            System.out.println("Wątki uruchomione");
        }
    }
}

Czy synchronizacja związana z "blokada2" zapewnia że notifyAll() wykona się zawsze na końcu?
Chodzi mi o uniknięcie sytuacji kiedy notifyAll() wykona się jako 1 lub 2 wątek. Chce żeby zawsze był to ostatni.

Dodatkowo zauważyłem że notfiy() zawsze wybudza ten sam wątek. Około 30 prób. To samo z notifyAll() - zawsze wybudza wątki w tej samej kolejności. A powinno to być losowo.

Pozdrawiam
TR

2 odpowiedzi

+1 głos
odpowiedź 3 lipca 2018 przez mbabane Szeryf (79,280 p.)
wybrane 3 lipca 2018 przez Tomasz Rogalski
 
Najlepsza
Te metody raczej nie służą do takiej synchronizacji - pomimo tego że to działa. Ich zadaniem jest synchronizować dostęp do konkretnego obiektu, a użycie ich jako typowe mutex'y czy semafory jest według mnie śmierdzące. Zauważ, że te metody posiada każdy obiekt i nie są bezpośrednio związane z klasą Thread czy Java Concurrent API.
komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)
Ok, jak proponujesz zrobić to lepiej żeby moje założenie zostało spełnione?
komentarz 3 lipca 2018 przez mbabane Szeryf (79,280 p.)
A mógłbyś to jakoś inaczej opisać bo tak na prawdę nie do końca rozumiem co masz na myśli.
komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)

Oryginalny kod wyglądał tak:

class Oczekujacy implements Runnable {

    private final Object blokada;

    public Oczekujacy(Object blokada) {
        this.blokada = blokada;
    }

    @Override
    public void run() {
        synchronized (blokada) {
            System.out.println(Thread.currentThread().getName() + ": Wejście do sekcji synchronizowanej");
            try {
                blokada.wait();
                System.out.println(Thread.currentThread().getName() + ": Oczekiwanie");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": Wykonanie sekcji krytycznej");
        }
    }
}

class Dostarczajacy implements Runnable {

    private Object blokada;

    public Dostarczajacy(Object blokada) {
        this.blokada = blokada;
    }

    @Override
    public void run() {
        synchronized (blokada) {
            System.out.println("Wybudzam wszytskie wątki");
            blokada.notifyAll();
        }
    }
}

class Testowa {

    public static void main(String[] args) throws InterruptedException {
        Object blokada = new Object();
        Oczekujacy oczekujacy_1 = new Oczekujacy(blokada);
        new Thread(oczekujacy_1, "1").start();

        Oczekujacy oczekujacy_2 = new Oczekujacy(blokada);
        new Thread(oczekujacy_2, "2").start();

        Dostarczajacy dostarczajacy = new Dostarczajacy(blokada);
        new Thread(dostarczajacy).start();
        System.out.println("Wątki uruchomione");
    }
}

Jak widać to zwykły kod uruchamiający 3 wątki: 2 uśpione, 1 wybudzający.

To co chce zrobić, to przerobić to tak że by mieć 100% pewności że wątek wybudzający wykona się jako ostatni. Jak widzisz w chwili obecnej może być tak że wątek wybudzający wykona się pierwszy i program się zawiesi.

Pomysły, sugestie?

komentarz 3 lipca 2018 przez mbabane Szeryf (79,280 p.)

Na przykład tak:

import java.util.concurrent.Semaphore;

class Sleeper implements Runnable
{
   final Semaphore semaphore;
    String name;

    public Sleeper(Semaphore semaphore, String name)
    {
        this.semaphore = semaphore;
        this.name = name;
    }

    @Override
    public void run()
    {
        try
        {
            System.out.println(name + ": czeka na dostęp");
            semaphore.acquire();
            System.out.println(name + ": Wybudzony");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

class Alarm implements Runnable
{
    private final Semaphore semaphore;

    public Alarm(Semaphore semaphore)
    {
        this.semaphore = semaphore;
    }

    @Override
    public void run()
    {
        try
        {
            Thread.sleep(1000);
            System.out.println("Wybudzam");
            semaphore.release(2);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

public class NotifWatingTest
{
    public static void main(String[] args) throws Exception
    {
        Semaphore semaphore = new Semaphore(0);

        new Thread(new Sleeper(semaphore, "A")).start();
        new Thread(new Sleeper(semaphore, "B")).start();
        new Thread(new Alarm(semaphore)).start();

        Thread.sleep(3000);
        System.out.println("Koniec");
    }
}
komentarz 3 lipca 2018 przez mbabane Szeryf (79,280 p.)
edycja 3 lipca 2018 przez mbabane

Chyba, że chodzi Ci też o to, że to te 2 wątki mają wysłać komunikat do tego 3 "hej możesz ruszać", bo w poprzednim przykładzie jest tak ze te 2 czekają aż Alarm da sygnał. Jeśli tak to wtedy trzeba np. tak:

import java.util.concurrent.Semaphore;

class Sleeper implements Runnable
{
   final Semaphore semaphore;
    String name;

    public Sleeper(Semaphore semaphore, String name)
    {
        this.semaphore = semaphore;
        this.name = name;
    }

    @Override
    public void run()
    {
        try
        {

            Thread.sleep(1000);
            System.out.println(name + ": Wykonywanie");
            semaphore.release();
            System.out.println(name + ": Wyslanie sygnalu o zakonczeniu");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }


    }
}

class Alarm implements Runnable
{
    private final Semaphore semaphore;

    public Alarm(Semaphore semaphore)
    {
        this.semaphore = semaphore;
    }

    @Override
    public void run()
    {
        try
        {
            System.out.println("Czeka na sygnal od 2 innych watków");
            semaphore.acquire();
//            Thread.sleep(10);
            System.out.println("Wybudzony: Wykonywanie");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

public class NotifWatingTest
{
    public static void main(String[] args) throws Exception
    {
        Semaphore semaphore = new Semaphore(-1);

        new Thread(new Sleeper(semaphore, "A")).start();
        new Thread(new Sleeper(semaphore, "B")).start();
        new Thread(new Alarm(semaphore)).start();

        Thread.sleep(3000);
        System.out.println("Koniec");
    }
}

To samo można osiągnąć używając trochę ciekawszego narzędzia: klasy CyclicBarrier. Działa ona tak, że ustawiamy ile cykli ma zostać zgłoszonych (wykonanych) i akcję jaka ma się uruchomić (czyli wątek) po dokonaniu tych cykli.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

class SomeThread implements Runnable
{
   final CyclicBarrier cyclicBarrier;
    String name;

    public SomeThread(CyclicBarrier cyclicBarrier, String name)
    {
        this.cyclicBarrier = cyclicBarrier;
        this.name = name;
    }

    @Override
    public void run()
    {
        try
        {
            System.out.println(name + ": Wykonywanie");
            Thread.sleep(1000);
            System.out.println(name + ": Wyslanie sygnalu o zakonczeniu");
            cyclicBarrier.await();

        }
        catch (InterruptedException | BrokenBarrierException e)
        {
            e.printStackTrace();
        }
    }
}

class Action implements Runnable
{
    @Override
    public void run()
    {
        System.out.println("Akcja");
    }
}

public class NotifWatingTest
{
    public static void main(String[] args) throws Exception
    {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Action());

        new Thread(new SomeThread(cyclicBarrier, "A")).start();
        new Thread(new SomeThread(cyclicBarrier, "B")).start();

        Thread.sleep(3000);
        System.out.println("Koniec");
    }
}
komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)
edycja 3 lipca 2018 przez Tomasz Rogalski

Chyba, że chodzi Ci też o to, że to te 2 wątki mają wysłać komunikat do tego 3 "hej możesz ruszać"

Tak, tego chcę. I ten przykład z semaphore wygląda dobrze. CyclicBarrier też fajne. Dzięki wielkie!

komentarz 3 lipca 2018 przez Wiciorny Ekspert (269,590 p.)
fajnie panowie ze mozna troszeczke odświeżyć i poczytać :) dawno z wątkami nie miałem doczynienia w takich  prymitywnych a nie do konca oczywistych sytuacjach
komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)
Polecam się. Jeszcze przy okazji OCP zajrzę tu z pytaniami niejednokrotnie. Będzie okazja podyskutować.
komentarz 3 lipca 2018 przez Wiciorny Ekspert (269,590 p.)
a  OCP to swoją drogą, egzamin dosyć prosty... bo to takie struktualne rzeczy malo projektowe ale pracująć na co dzien zapomina się o tym totalnie.
komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)
edycja 5 lipca 2018 przez Tomasz Rogalski
Zgadza się.
0 głosów
odpowiedź 2 lipca 2018 przez Wiciorny Ekspert (269,590 p.)

Siema na wstepie::

metody: wait() / notifyAll() 


Po pierwsze wolno tych metod używać tylko w sekcji synchronized, tudzież w metodach synchronizowanych i wołać je wolno tylko z obiektów na których odbywa się synchronizacja.

 Co robi notifyAll()? notifyAll(). Ma więc obudzić wszystkie wątki które czekają na sygnał na danym monitorze. Czyli jeśli jakiś wątek czeka w kolejce, ze względu na blokade w bloku synchronizowanym to zostanie on uruchomiony. 

 

Czy synchronizacja związana z "blokada2" zapewnia że notifyAll() wykona się zawsze na końcu? 

wykona się w miejscu którym zaznaczyłeś, tutaj  podczas bloku synchronizowanego kiedy go powołasz 

 

komentarz 3 lipca 2018 przez Tomasz Rogalski Bywalec (2,800 p.)
Witam, dzięki za odpowiedź aczkolwiek nie odpowiada na pytania:) wiem co robią użyte tu komendy:)
komentarz 3 lipca 2018 przez Wiciorny Ekspert (269,590 p.)

nie komendy, a metody. Tzn  jeśli wiedziałbyś, to twoje pytanie mija się z celem. Czemu? Bo pytasz- czy wykona się zawsze- tak, zawsze wykona. To jest niezależne 

Chodzi mi o uniknięcie sytuacji kiedy notifyAll() wykona się jako 1 lub 2 wątek. Chce żeby zawsze był to ostatni. 

to zależy także od twojej implementacji i tego jak wywołasz wątki .... 

Wszystkie wątki będące w kolejce podczas synchronizacji bedą uruchomione... po tej metodzie, kwestia ich kolejnosci to kwestia powołania ich "do kolejki' 1 w kolejce- 1 na wyjściu , a zakończenie zależy od ich czasu bo jesli 2 wątek jest dłuższy niż 1 w kolejce (   jego złożoność czasowa i lub obliczeniowa jest diametralnie większa ) to to zakończy się jako ostatni 

Podobne pytania

0 głosów
1 odpowiedź 315 wizyt
pytanie zadane 11 listopada 2018 w C i C++ przez matiibal Użytkownik (620 p.)
0 głosów
1 odpowiedź 225 wizyt
0 głosów
1 odpowiedź 1,806 wizyt
pytanie zadane 24 listopada 2015 w Java przez Mikrokontroler xD Stary wyjadacz (13,500 p.)

92,539 zapytań

141,382 odpowiedzi

319,479 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...