Threads und synchronized - Verständnisproblem

  • Themenstarter Themenstarter xhi2018
  • Beginndatum Beginndatum
X

xhi2018

Gast
Hallo,

ich hab' folgende drei Java Klassen:

Java:
public class FirstThreadProgramm {

   public static void main(String[] args) {
      System.out.println("Programmstart!");
      Counter cnt = new Counter();
      Thread t1 = new Thread(new CountThread("1", cnt));
      Thread t2 = new Thread(new CountThread("2", cnt));
      Thread t3 = new Thread(new CountThread("3", cnt));
      Thread t4 = new Thread(new CountThread("4", cnt));
      t1.start();
      t2.start();
      t3.start();
      t4.start();
      try {
         t1.join();
         t2.join();
         t3.join();
         t4.join();
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }     
      System.out.println("Programmende!");
   }
}
Java:
public class CountThread implements Runnable {

   private String number;
   private Counter counter;
   private Integer i;
   
   public CountThread(String nr, Counter cnt) {
      this.number = nr;
      this.counter = cnt;
      i = 0;
   }

   @Override
   public String toString() {
      return this.getClass().getSimpleName() + " [ " + number;
   }
   
   public void run() {
      while ( counter.increase() ) {
         System.out.println(toString() + " ] - " + counter.toString() );
         i ++;
      }
      System.out.println(toString() + " ] wurde [ " + i.toString() + " ] mal ausgeführt!");
   }
}
Java:
public class Counter {
   
   private Integer counter = 0;
   private final Integer max = 40;
   
   /**
    * @return <b>true</b> wenn der maximale Wert des Counter Objects nicht 
    * erreicht ist ansonsten <b>false</b>
    */
   public synchronized boolean increase() {
      counter = Math.min( max, counter );
      return ( max > counter++ );
   }

   @Override
   public synchronized String toString() {
      return this.getClass().getSimpleName() + " [ " + counter.toString() + " ] ";
   }
}
nun bekomme ich folgende Ausgabe (natürlich sind die Zahlen bei jedem Aufruf unterschiedlich):
Code:
Programmstart!
CountThread [ 1 ] - Counter [ 1 ] 
CountThread [ 1 ] - Counter [ 2 ] 
...
CountThread [ 1 ] - Counter [ 21 ] 
...
CountThread [ 3 ] - Counter [ 38 ] 
CountThread [ 3 ] - Counter [ 40 ] 
CountThread [ 3 ] wurde [ 5 ] mal ausgeführt!
CountThread [ 1 ] - Counter [ 36 ] 
CountThread [ 4 ] wurde [ 0 ] mal ausgeführt!
CountThread [ 2 ] - Counter [ 38 ] 
CountThread [ 2 ] wurde [ 20 ] mal ausgeführt!
CountThread [ 1 ] wurde [ 15 ] mal ausgeführt!
Programmende!

Was ich nicht verstehe ist, dass beim Zähler des Counter Objekts eigentlich immer eine Zahl doppelt ist - im obigen Fall ist es die Zahl 38 - und dafür aber eine Zahl fehlt, was jetzt nicht zu sehen ist, weil ich die Ausgabe gekürzt habe. Die Threads werden aber in Summe - wie gewünscht - zusammen 40 mal ausgeführt.

Aber warum ist das so ???:L Ich dachte mit dem
Code:
synchronized
würde genau so was verhindert, oder verstehe ich da was falsch?
Gruß und vielen Dank für Eure Hilfe / Erklärung!
 
Das Problem befindet sich in CounterThread...
[java=18] public void run() {
while ( counter.increase() ) {
System.out.println(toString() + " ] - " + counter.toString() );
i ++;
}
System.out.println(toString() + " ] wurde [ " + i.toString() + " ] mal ausgeführt!");
}[/code]
Der Aufruf von Zeile 19 (increase()) ist synchronisiert. Der Aufruf in Zeile 20 (toString()) ist ebenfalls synchronisiert. Zwischen den beiden Zeilen ist aber nichts synchronisiert. Wenn Thread A nun increase() aufruft, dann ein anderer Thread B increase() ruft und dann erst Thread A toString() ruft, dann erhält Thread A die durch Thread B veränderte Ausgabe. Du musst also den ganzen Bereich auf den Counter synchronisieren; das könnte bspw. so aussehen:
[java=18] public void run() {
while ( true ) {
synchronized (counter) {
if (!counter.increase()) {
break;
}
System.out.println(toString() + " ] - " + counter.toString() );
}
i ++;
}
System.out.println(toString() + " ] wurde [ " + i.toString() + " ] mal ausgeführt!");
}[/code]
Ebenius
 
Hallo,
Das Problem befindet sich in CounterThread...

Der Aufruf von Zeile 19 (increase()) ist synchronisiert. Der Aufruf in Zeile 20 (toString()) ist ebenfalls synchronisiert. Zwischen den beiden Zeilen ist aber nichts synchronisiert. Wenn Thread A nun increase() aufruft, dann ein anderer Thread B increase() ruft und dann erst Thread A toString() ruft, dann erhält Thread A die durch Thread B veränderte Ausgabe. Du musst also den ganzen Bereich auf den Counter synchronisieren;

😳 darauf wäre ich jetzt von selber nicht gekommen - ich werd' das später ausprobieren.
:toll: super & schon mal vielen Dank :applaus:
 
Du musst also den ganzen Bereich auf den Counter synchronisieren; das könnte bspw. so aussehen:
[java=18] public void run() {
while ( true ) {
synchronized (counter) {
if (!counter.increase()) {
break;
}
System.out.println(toString() + " ] - " + counter.toString() );
}
i ++;
}
System.out.println(toString() + " ] wurde [ " + i.toString() + " ] mal ausgeführt!");
}[/code]
prima! genau so funktioniert es wie von mir gewünscht...

super & danke nochmal für Deine Hilfe :toll:
 
Zuletzt bearbeitet von einem Moderator:

Zurück
Oben