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

question-closed Synchronizacja Java Wątki

Object Storage Arubacloud
+1 głos
578 wizyt
pytanie zadane 29 grudnia 2016 w Java przez Wiciorny Ekspert (270,150 p.)
zamknięte 29 grudnia 2016 przez Wiciorny

Witam Serdecznie. Mam problem z synchronizacją watków. 

Program tutaj - oparty jest o dwa wątki,  rezultatem jest debet, Mam zastosować synchronizacje tak, aby debetu nie było na koncie ...

Nie mam zielonego pojęcia jak to zrobić, próbowałem oznaczać metode [RUN, MySleep etc] jako synchronized, ale to nie przynosiło rezultatu. Chodzi mi o fakt, kiedy wątek jeden skorzysta z konta [ tutaj 120 ] to po jego pracy zmniejszy sie wartosc pieniedzy na koncie. Z koleji drugi watek korzystac powinien wtedy z tego juz zmienionego zasobu co spowoduje że warunek if sie nie wykona. 

Any examples? Dziękuje za poświęcony czas i pomoc. 

class MySleep {
   public static void sleep( int i ) {
     try {
       Thread.sleep( i );
      }
     catch ( InterruptedException e ) {}
   }
}

class Konto {
   private double stan = 0.0;
   void   wplata( double ile )  { stan += ile; }
   void   wyplata( double ile ) { stan -= ile; }
   double stanKonta()           { return stan; }
}

class Zakupy implements Runnable {
   private Konto p;
   
   public void maszKonto( Konto p ) { this.p = p; };
   public void run() {
      double cena = 77.78;            // znam cene towaru
      MySleep.sleep( 2000 );          // chwila na zastanowienie...
      if ( p.stanKonta() > cena ) {   // sprawdzam czy mam pieniadze na koncie 
         System.out.println( "Tu ja " + Thread.currentThread().getName() + " ide do kasy zaplacic" );
         MySleep.sleep( 5000 );       // ide do kasy
         p.wyplata( cena );           // wyplata pieniedzy 
      }
   }
}

class Start {
   public static void main( String[] argv ) {
      Konto k = new Konto();
      k.wplata( 120.0 );
      
      Zakupy zakupyJasia = new Zakupy();
      zakupyJasia.maszKonto( k );
      
      Zakupy zakupyMarysi = new Zakupy();
      zakupyMarysi.maszKonto( k );
      
      Thread watekJasia = new Thread( zakupyJasia );
      watekJasia.setName( "watek Jasia" );

      Thread watekMarysi = new Thread( zakupyMarysi );
      watekMarysi.setName( "watek Marysi" );

      watekMarysi.start();
      watekJasia.start();

      System.out.println( "Wlasnie uruchomione zostaly 2 odrebne watki - czekamy na rezultat" );
      
      MySleep.sleep( 10000 );
      
      System.out.println( "Sprawdzamy stan konta: " + k.stanKonta() ); 
      
      if ( k.stanKonta() < 0 ) { // tu nalezy wyrazic zdziwienie
         System.out.println( "Przecież sie umawialiśmy - przed zakupem masz sprawdzić czy są pieniędze na koncie !" );
         System.out.println( "A jest DEBET !!!!" );
      } 
   }
}

 

komentarz zamknięcia: Rozwiązany problem. Dla  innych, może się przydać rozwiązanie.

1 odpowiedź

0 głosów
odpowiedź 29 grudnia 2016 przez Wiciorny Ekspert (270,150 p.)
 
Najlepsza

Rozwiązałem problem. dodałem synchronizacje na obiekcie. 

synchronized(p){
   if ( p.stanKonta() > cena ) {   // sprawdzam czy mam pieniadze na koncie 
         System.out.println( "Tu ja " + Thread.currentThread().getName() + " ide do kasy zaplacic" );
         MySleep.sleep( 5000 );       // ide do kasy
         p.wyplata( cena );           // wyplata pieniedzy 
      }

	else
	{
		System.out.println("Za malo hajsu masz " + Thread.currentThread().getName());
	}
	}
	}

 

komentarz 12 stycznia 2017 przez Wojciech Cies Obywatel (1,140 p.)
A wiesz dlaczego działa?
komentarz 14 stycznia 2017 przez Wiciorny Ekspert (270,150 p.)
tak, nie wiem czy mam Ci wytłumaczyć? Czy jaki jest cel twojego pytania?
komentarz 15 stycznia 2017 przez Wojciech Cies Obywatel (1,140 p.)

Celem jest dowiedzenie się, czy jeszcze czegoś nie wyjaśnić :) - spokojnie.

Twoje rozwiązanie ma wadę - bardzo złą praktyką (rozumiem, że to jest teraz nauka i eksperyment, ale warto wiedzieć na przyszłość) jest synchronizowanie się na obiekcie spoza tego obiektu. Łatwo może to doprowadzić do nieprzewidzianych deadlocków w kodzie, ponieważ autor klasy nie wie kto i gdzie zabierze mu monitor (mutexa). 

Twoje rozwiązanie działałoby tak samo, jakbyś dodał do metod wplata/wyplata/konto słowo kluczowe synchronized. Jest to tożsame ze zrobieniem synchronized(this) { ciało metody }.

 

komentarz 15 stycznia 2017 przez Wiciorny Ekspert (270,150 p.)
tylko zwróć uwagę że takie dodawanie jest "code smells" moim zdaniem to raz a dwa nie zakladamy ze wszystko chcemy z obiektu synchronizowac
komentarz 15 stycznia 2017 przez Wojciech Cies Obywatel (1,140 p.)
edycja 15 stycznia 2017 przez Wojciech Cies

synchronizacja na obiekcie spoza tego obietku code smellem nie jest, a metody synchronizowane są? Żartujesz :) Od kiedy dodanie synchronized do metody jest code smell? Pewnie, że lepiej używać narzędzi wyższego rzędu (java.util.concurrent.*) zamiast babrać się w ręczną synchronizację. Ale synchronized w prostych scenariuszach użyć - zwłaszcza w kodzie "do nauki" można.

Powinieneś ukryć stan i uczynić obiekt Konto "bezpiecznym wątkowo" (thread safe). Powinieneś wprowadzić w nim synchronizowaną metodę "wyplacJezeliMaszHajs" zwracającą np. stan konta po operacji i rzucającą wyjątkiem, jeżeli saldo jest niewystarczające. Albo zwracającą boolean mówiący czy się udało, czy nie.

Nie mówiąc, że Twoje rozwiązanie jest do niczego, jeżeli obiekt Konto jest dostępny poza tą metodą zawierającą synchronized. Co z tego, że założyłeś sobie lock na obiekcie (przez synchronized) jak ktoś inny z innego wątku może spokojnie w tym czasie wywołać wyplata(200) i Twój kod nadal pozwoli na dokonanie debetu?

 

class MySleep {
  public static void sleep(int i) {
    try {
      Thread.sleep(i);
    } catch (InterruptedException e) {
      // zawsze rzucaj wyjątek lub loguj. Zjadanie to zbrodnia.
      throw new RuntimeException(e);
    }
  }
}

class Konto {
  private double stan = 0.0;

  public synchronized boolean wyplataJezeliSaSrodki(double kwota) {
    // dodaj sprawdzenie, czy kwota jest nieujemna!
    if (stan >= kwota) {
      stan -= kwota;
      return true;
    } else {
      return false;
    }
  }

  synchronized void wplata(double ile) {
    // dodaj sprawdzenie, czy kwota jest nieujemna!
    stan += ile;
  }

  synchronized void wyplata(double ile) {
    // dodaj sprawdzenie, czy kwota jest nieujemna!
    stan -= ile;
  }

  synchronized double stanKonta() {
    return stan;
  }
}

class Zakupy implements Runnable {
  private Konto p;

  public void maszKonto(Konto p) {
    this.p = p;
  }

  ;

  public void run() {
    double cena = 77.78;            // znam cene towaru
    MySleep.sleep(2000);          // chwila na zastanowienie...
    if (p.stanKonta() > cena) {   // sprawdzam czy mam pieniadze na koncie
      System.out.println("Tu ja " + Thread.currentThread().getName() + " ide do kasy zaplacic");
      MySleep.sleep(5000);       // ide do kasy
      if (!p.wyplataJezeliSaSrodki(cena)) {
        System.out.println("Nie udalo sie wyplacic...");
      }
      ;           // wyplata pieniedzy
    }
  }
}

class Start {
  public static void main(String[] argv) {
    Konto k = new Konto();
    k.wplata(120.0);

    Zakupy zakupyJasia = new Zakupy();
    zakupyJasia.maszKonto(k);

    Zakupy zakupyMarysi = new Zakupy();
    zakupyMarysi.maszKonto(k);

    Thread watekJasia = new Thread(zakupyJasia);
    watekJasia.setName("watek Jasia");

    Thread watekMarysi = new Thread(zakupyMarysi);
    watekMarysi.setName("watek Marysi");

    watekMarysi.start();
    watekJasia.start();

    System.out.println("Wlasnie uruchomione zostaly 2 odrebne watki - czekamy na rezultat");

    MySleep.sleep(10000);

    System.out.println("Sprawdzamy stan konta: " + k.stanKonta());

    if (k.stanKonta() < 0) { // tu nalezy wyrazic zdziwienie
      System.out.println("Przecież sie umawialiśmy - przed zakupem masz sprawdzić czy są pieniędze na koncie !");
      System.out.println("A jest DEBET !!!!");
    }
  }
}

 

Podobne pytania

0 głosów
1 odpowiedź 211 wizyt
pytanie zadane 24 maja 2017 w Java przez gaaf Nowicjusz (140 p.)
0 głosów
1 odpowiedź 136 wizyt
pytanie zadane 26 kwietnia 2018 w C i C++ przez Storm Obywatel (1,570 p.)
0 głosów
1 odpowiedź 922 wizyt

92,572 zapytań

141,423 odpowiedzi

319,645 komentarzy

61,959 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!

...