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

Komunikacja między wątkami

VPS Starter Arubacloud
0 głosów
904 wizyt
pytanie zadane 21 kwietnia 2019 w Java przez poldeeek Mądrala (5,980 p.)
Witam chciałem napisać prosty program posiadający 2 wątki, które np losują liczby do 100. Jeśli któryś z nich wylosuje pierwszy liczbę > 50 to wysyła jakiś sygnał do 2 wątku i tamten przerywa działanie wystawiając komunikat o przegranej, a wątek który wylosował >50 o swojej wygranej. I teraz pytanie w jaki sposób wątki w javie mogą się ze sobą komunikować, ponieważ te metody notify() i wait() średnio tu będą działały z racji tego, że te wątki nie będą miały swojego wspólnego obiektu tylko każdy będzie miał tą jedną swoją zmienną do losowania...

3 odpowiedzi

+1 głos
odpowiedź 22 kwietnia 2019 przez hans001 Obywatel (1,150 p.)
Myśle że najlepszym rozwiązaniem jest rozszerzenie klasy Thread i przez konstruktor przekazanie tego drugiego wątku. Wtedy będziesz go mógł zatrzymać używając innego wątku.
+1 głos
odpowiedź 22 kwietnia 2019 przez mbabane Szeryf (79,280 p.)
Tak sobie zadaje pytanie czy takie coś jest aby na pewno możliwe. Co mam na myśli. Wydaje mi się, że dany wątek nie jest w stanie jednocześnie losować liczby i oczekiwać wiadomości od innego wątku. Bo albo ma losować albo oczekiwać.Według mnie tutaj przydał by się jeszcze trzeci wątek na zasadzie sędziego. I ten 3 wątek oczekuje na sygnał od tzw. pierwszego lepszego.
+1 głos
odpowiedź 29 kwietnia 2019 przez Avodaya Obywatel (1,200 p.)

A ja mam prostszy pomysł, zrób dwie zmienne statyczne 

static boolean w1 = false, w2 = false;

W pętli pewnie masz losowanie, sprawdzenie wyniku.

Dodaj:jeśli wynik1 > 50 to wtedy w1 = true.

A całą pętle zrób w while w2 == false;

Po pętli dopisz sprawdzenie czy w1 == true jeśli tak to w1 wygrywa, jeśli nie to przegrywa.

I drugi tak samo.

komentarz 2 maja 2019 przez mbabane Szeryf (79,280 p.)

Dany wątek będzie tylko setował w1? Więc de-facto dwa wątki będą cały czas setować w1, a w2 będzie setowane kiedy?

Żeby to zadziałało pewnie będzie trzeba zrobić kilka dziwnych ifów.

Powyższe zadanie w javie można zrobić bardzo prosto, wykorzystując interfejs Callable (jest to jakby Runnable, który może zwracać dodatkowo jakąś wartość) i narzędzia z pakietu java.util.concurrent:

public class RandomGame2
{
    public static void main(String[] args) throws Exception
    {
        List<Callable<Player>> tasks = List.of(
                new Player("Player 1"),
                new Player("Player 2")
        );
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Player winner = executor.invokeAny(tasks); // to czeka na pierwszego lepszego

        System.out.println("Wygrał: " + winner.getName());
        executor.shutdown();

    }
}


class Player implements Callable<Player>
{
    private String name;
    private Random random = new Random();

    public Player(String name)
    {
        this.name = name;
    }

    @Override
    public Player call() throws Exception
    {
        System.out.println(name() + "Start");
        int value = 0;

        while (value != 5)
        {
            value = random.nextInt(20);
            System.out.println(name() + " - value: " + value);
        }
        System.out.println(name() + "End");
        return this;
    }

    private String name()
    {
        return String.format("[%s]:", name);
    }

    public String getName()
    {
        return name;
    }
}

Początkowo zrobiłem to z wykorzystaniem klasy Exchanger, która umożliwia wymianę (dosłownie) danych między wątkami. Minus tego jest taki, że taka wymiana wymusza synchronizacje między nimi. Czyli ten ktory pierwszy wylosuje będzie oczekiwał na los drugiego (jakby sprawdzają siebie na wzajem po każdym losie, co wydaje mi się, że może trochę przekłamywać wynik). Coś takiego:

public class RandomGame
{
    public static void main(String[] args) throws Exception
    {
        Exchanger<Integer> exchanger = new Exchanger<>();
        Thread player1 = new Thread(new Player3(exchanger, "Player 1"));
        Thread player2 = new Thread(new Player3(exchanger, "Player 2"));

        player1.start();
        player2.start();

        player1.join();
        player2.join();
    }
}

class Player3 implements Runnable
{
    private Exchanger<Integer> exchanger;
    private String player;

    public Player3(Exchanger<Integer> exchanger, String player)
    {
        this.exchanger = exchanger;
        this.player = player;
    }

    @Override
    public void run()
    {
        int value = 0;
        int exchange = 0;
        Random random = new Random();
        while (value != 5 && exchange != 5)
        {
            value = random.nextInt(10);
            System.out.println(name() + " - value:" + value);
            try
            {
                exchange = exchanger.exchange(value);
                System.out.println(name() + " - orzymal: " + exchange);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }

        System.out.println(name() + " - end");
        System.out.println(name() + " - end value:" + value);
        System.out.println(name() + " - end exchanger: " + exchange);
    }

    private String name()
    {
        return String.format("[%s]", player);
    }
}

Może być też drobny problem ze wskazaniem, kto dokładnie wygrał. (Może też kwestia tych beznadziejnych logów robionych na szybko).

komentarz 2 maja 2019 przez Avodaya Obywatel (1,200 p.)
edycja 2 maja 2019 przez Avodaya

#edit: dodałem wskazanie zwycięzcy.

Nie wydaje mi się żebym użył dziwnych ifów, zerknij sobie. Chodziło mi o takie coś:

import java.util.Random;

public class RandomGame {
	
	
    public static void main(String[] args) throws Exception {
         Thread player1 = new Thread(new Player3("Player 1", 1));
        Thread player2 = new Thread(new Player3("Player 2", 2));
 
        player1.start();
        player2.start();
 
        player1.join();
        player2.join();
    }
}
 
class Player3 implements Runnable {
    private String player;
	private int w;
	static boolean w1 = false, w2 = false;
	
    public Player3(String player, int w) {
        this.player = player;
		this.w = w;
    }
 
    @Override
    public void run() {
        int value = 0;
        int exchange = 0;
        Random random = new Random();
        while (w1 == false && w2 == false) {
            value = random.nextInt(10);
            System.out.println(name() + " - value:" + value);
			if (value == 5) {
				switch (w) {
					case 1:
						w1 = true;
						break;
					case 2:
						w2 = true;
						break;
				}
			}
		} 
    System.out.println(name() + " - end");
    System.out.println(name() + " - end value:" + value);
	if (value == 5) System.out.println("End the winner is.... " + name() + " !!");
	}
    
 
    private String name() {
        return String.format("[%s]", player);
    }

}

 

komentarz 2 maja 2019 przez mbabane Szeryf (79,280 p.)

No nie wiem, to nie wygląda zbyt dobrze.

if (value == 5) {
                switch (w) {
                    case 1:
                        w1 = true;
                        break;
                    case 2:
                        w2 = true;
                        break;
                }
            }
        } 

Plus na koncu to:

 if (value == 5) System.out.println("End the winner is.... " + name() + " !!");
    }

Dodatkowo powyższy if sprawdza tylko, czy value jest na 5, a nie kto pierwszy to osiągnął. A zrobienie np. takiego czegoś:

if(w1) 
   sout("Wygrywa w1");
else 
  sout("Wygyrwa w2");

Nie powoduje, że drugi wątek przestaje pracować i w rezultacie będziesz miał komunikat o dwóch zwycięzcach.

Inną rzeczą jest też że kod:

 case 2:
        w2 = true;
       break;
}

Jest zbędny z punktu widzenia, wątku 1 (JIT to pewnie wykosi, ale my go widzimy i (przynajmniej ja) się krzywimy).

To już wole rozwiązanie przekazujące cały wątek jeden do drugiego. Mimo, że też nie jest zbyt eleganckie bo jeden wątek wie wszystko na temat drugiego.

 

Najciekawszym rozwiązaniem jest chyba to z Callable. Bo zauważ,ze jest mega łatwo rozszerzalne, można sobie zrobić np. 10 graczy, bez dopisywania logiki, bo robisz tak:

 List<Callable<Player>> tasks = List.of(
         new Player("Player 1"),
         new Player("Player 2"),
         new Player("Player 3"),
         new Player("Player 4"),
         .
         .
         new Player("Player 10)
);

ExecutorService executor = Executors.newFixedThreadPool(10);

Reszta bez zmian.

komentarz 3 maja 2019 przez Avodaya Obywatel (1,200 p.)

Myślałem już wcześniej w trakcie pisania o rozszerzalności (stąd też case2 zamiast ifelse), faktycznie dla mojej metody z wykorzystaniem statycznych zmiennych ciężko mówić o łatwym dodaniu kolejnych wątków.

Sprawdziłem to co opisałeś z Callable.

Myślałeś o kończeniu pozostałych wątków po znalezieniu zwycięzcy?
Dodałem w pętli losującej value:

if (Thread.currentThread().isInterrupted()) {
        throw new RuntimeException(); 
    }

Ale po wylosowaniu zwycięzcy pozostałe wątki kończą ostatnie losowanie (jeden nadmiarowy wynik).

Co do synchronizacji to bez niej jest dziwnie - chyba że o to Ci chodziło. Samo losowanie w każdym wątku jest niezależne i przykładowo w trakcie jednej gry dla 4 wątków:

  • Player1 wyników: 9,
  • Player2 wyników: 5,
  • Player3 wyników: 9,
  • Player4 wyników: 4.

 

komentarz 3 maja 2019 przez mbabane Szeryf (79,280 p.)

Jeśli chodzi o:

Myślałeś o kończeniu pozostałych wątków po znalezieniu zwycięzcy? 

to w dokumentacji dla metody invokeAny jest zapisane:

Upon normal or exceptional return, tasks that have not completed are cancelled. 

https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/ExecutorService.html#invokeAny(java.util.Collection)

 

Chyba nie rozumiem tego:

Ale po wylosowaniu zwycięzcy pozostałe wątki kończą ostatnie losowanie (jeden nadmiarowy wynik). 

 

Podobne pytania

0 głosów
1 odpowiedź 169 wizyt
pytanie zadane 29 grudnia 2018 w Java przez ILikeJava Obywatel (1,230 p.)
+1 głos
1 odpowiedź 742 wizyt
pytanie zadane 19 lipca 2015 w C i C++ przez niezalogowany

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 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

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 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!

...