Benutzer-Werkzeuge

Webseiten-Werkzeuge


api:documentation:threads:start

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
api:documentation:threads:start [2024/10/28 13:46] – [Race condition und Lösungen] martinapi:documentation:threads:start [2025/03/05 10:26] (aktuell) – [synchronized-Block] martin
Zeile 121: Zeile 121:
 {{ :api:documentation:threads:race_condition.svg?400|race condition sequence diagram}} {{ :api:documentation:threads:race_condition.svg?400|race condition sequence diagram}}
 Im rechts stehenden Sequenzdiagramm ist zu sehen, was passiert, wenn zwei Threads gleichzeitig die Methode ''increment()'' des ''Counter''-Objekts aufrufen. Stellen Sie sich vor, das Attribut ''counter'' hat zu Beginn den Wert 4. Der blaue Thread ist etwas früher dran als der grüne, trotzdem erfolgt die Anweisung ''counter = i'' des blauen Threads erst nach der Anweisung ''i = counter'' des grünen Threads. Das hat zur Folge, dass sowohl die grüne Variable ''i'' als auch die blaue jeweils auf 5 erhöht werden. Am Ende wird zweimal derselbe Wert 5 in die Variable ''counter'' geschrieben, so dass sie folglich nur um eins erhöht wurde, obwohl die Methode ''increment()'' zweimal aufgerufen wurde. \\ \\  Im rechts stehenden Sequenzdiagramm ist zu sehen, was passiert, wenn zwei Threads gleichzeitig die Methode ''increment()'' des ''Counter''-Objekts aufrufen. Stellen Sie sich vor, das Attribut ''counter'' hat zu Beginn den Wert 4. Der blaue Thread ist etwas früher dran als der grüne, trotzdem erfolgt die Anweisung ''counter = i'' des blauen Threads erst nach der Anweisung ''i = counter'' des grünen Threads. Das hat zur Folge, dass sowohl die grüne Variable ''i'' als auch die blaue jeweils auf 5 erhöht werden. Am Ende wird zweimal derselbe Wert 5 in die Variable ''counter'' geschrieben, so dass sie folglich nur um eins erhöht wurde, obwohl die Methode ''increment()'' zweimal aufgerufen wurde. \\ \\ 
-Man nennt diesen Effekt eine **race condition**. \\ \\  +Man nennt diesen Effekt eine **race condition**. Sie entsteht dadurch, dass die Anweisungen in der Methode ''increment()'' von mehr als einem Thread gleichzeitig ausgeführt werden. Man spricht von einem **kritischen Abschnitt**.   
-**Abhilfe: synchronized-Methode** \\  +</WRAP> 
-Setzt man vor die Deklaration der Methode ''increment'' das Schlüsselwort ''synchronized'', so sorgt der Compiler dafürdass zur Laufzeit jeweils nur ein Thread die Methode "betreten" darf. Nachfolgende Threads werden **blockiert** (d.h. in der Ausführung angehalten) bis der erste Thread die Methode wieder verlassen hat. Erst dann wird einer der blockierten Threads wieder fortgesetzt.+ 
 +===== Synchronized-Methode ===== 
 +<WRAP center round info 80%> 
 +{{ :api:documentation:threads:synchronized_method.svg?400|synchronized method sequence diagram}} 
 +Wir brauchen eine Möglichkeit, um zu verhindern, dass mehr als ein Thread gleichzeitig den **kritischen Abschnitt** (in unserem Fall die Methode ''increment'') betritt. Dies leistet das Schlüssel wort ''synchronized'' bei der Deklaration der Methode ''increment''. Es weist den Compiler anCode zu generieren, der zur Laufzeit sicherstellt, dass jeweils nur einen Thread die Methode "betreten" darf. Nachfolgende Threads werden **blockiert** (d.h. in der Ausführung angehalten) bis der erste Thread die Methode wieder verlassen hat. Erst dann wird einer der blockierten Threads wieder fortgesetzt.
   * Vergleichen Sie die Deklaration der Methode ''increment'' im folgenden Programm mit der Deklaration im vorangegangenen Programm.   * Vergleichen Sie die Deklaration der Methode ''increment'' im folgenden Programm mit der Deklaration im vorangegangenen Programm.
   * Starten Sie das Programm mehrmals. Erklären Sie die Ausgabe!   * Starten Sie das Programm mehrmals. Erklären Sie die Ausgabe!
Zeile 180: Zeile 184:
  
 </HTML> </HTML>
 +
 +
 +===== Die Klasse Semaphore =====
 +<WRAP center round info 80%>
 +Ein **Semaphor** ist ein Objekt, das einen Zähler enthält, dessen Wert angibt, von wie vielen Prozessen ein kritischer Abschnitt im Augenblick gerade noch betreten werden darf. \\  (**Bem.:** Eine andere Interpretation besteht darin, dass es $n$ gleichartig Ressourcen gibt. Der Wert des Zählers gibt an, wie viele im Augenblick gerade zur Verfügung stehen.) \\ \\ Der Semaphor besitzt zwei Methoden:
 +  * ''aquire()'' prüft, ob der Zähler größer als 0 ist. 
 +    * Ist dies der Fall, so wird der Zähler um eins erniedrigt und mit der danach folgenden Anweisung fortgefahren. Dem Prozess wird damit das Betreten des kritischen Bereichs gestattet.
 +    * Ist dies **nicht** der Fall, so wird der Prozess in einen Wartezustand versetzt. \\ 
 +  * ''release()'' prüft, ob es zu diesem Semaphor Prozesse im Wartezustand gibt.
 +    * Falls dies der Fall ist, wir einer davon "aufgeweckt" und darf damit den kritischen Bereich betreten.
 +    * Ist dies **nicht** der Fall, so wird der Zähler um 1 erhöht.
 +</WRAP>
 +
 +<HTML>
 +
 +<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest2'}">
 +
 +<script type="text/plain" title="Semaphore1.java">
 +Counter counter = new Counter();
 +Semaphore semaphore = new Semaphore(1);
 +
 +for (int i = 0; i < 3; i++) {
 +   Incrementer inc = new Incrementer(counter, 1e6, i, semaphore);
 +   Thread t = new Thread(inc);
 +   t.setName("Thread " + i);
 +   t.start();
 +}
 +
 +class Counter {
 +   long counter = 0;
 +
 +   public synchronized void increment() {   // <-- mit Schlüsselwort synchronized!
 +      long i = counter;
 +      i++;
 +      counter = i;
 +   }
 +}
 +
 +
 +class Incrementer implements Runnable {
 +   
 +   Counter counter;
 +   long increment = 0;
 +   int index;
 +   Semaphore semaphore;
 +
 +   Incrementer(Counter counter, long increment, int index, Semaphore semaphore) {
 +      this.increment = increment;
 +      this.counter = counter;
 +      this.index = index;
 +      this.semaphore = semaphore;
 +   }
 +
 +   public void run() {
 +      for (long i = 0; i < increment; i++) {
 +         if(i % (increment / 10) == 0) println("Thread " + index + ": " + i);
 +         semaphore.aquire();
 +         counter.increment();
 +         semaphore.release();
 +      }
 +      println("Thread " + index + " done. Counter: " + counter.counter);
 +   }
 +
 +}
 +</script>
 +
 +</div>
 +
 +</HTML>
 +
 +
 +===== Monitorkonzept =====
 +<HTML>
 +
 +<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': true, 'id': 'ConsumerProducer134'}">
 +
 +<script type="text/plain" title="Main.java">
 +BlockingQueue<String> queue = new BlockingQueue<>(5);
 +
 +for (int i = 0; i < 3; i++) {
 +   new Producer(queue, 100, i);
 +   new MyConsumer(queue, 100, i);
 +}
 +
 +while (true);
 +
 +
 +class Producer implements Runnable {
 +   BlockingQueue<String> queue;
 +   int count;
 +   int producerIndex;
 +
 +   Producer(BlockingQueue<String> queue, int count, int producerIndex) {
 +      this.queue = queue;
 +      this.count = count;
 +      this.producerIndex = producerIndex;
 +      Thread t = new Thread(this);
 +      t.setName("Producer " + producerIndex);
 +      t.start();
 +   }
 +
 +   public void run() {
 +      while (count > 0) {
 +         queue.put(this.producerIndex + "/" + this.count);
 +         println("P " + this.producerIndex + " added " + count, Color.lightblue); 
 +
 +         count--;
 +      }
 +   }
 +
 +}
 +
 +class MyConsumer implements Runnable {
 +   BlockingQueue<String> queue;
 +   int count;
 +   int consumerIndex;
 +
 +   MyConsumer(BlockingQueue<String> queue, int count, int consumerIndex) {
 +      this.queue = queue;
 +      this.count = count;
 +      this.consumerIndex = consumerIndex;
 +      Thread t = new Thread(this);
 +      t.setName("Consumer " + consumerIndex);
 +      t.start();
 +   }
 +
 +   public void run() {
 +      while (count > 0) {
 +         String s = queue.take();
 +         println("C " + this.consumerIndex + " removed " + s, Color.lightgreen); 
 +         count--;
 +      }
 +   }
 +
 +}
 +</script>
 +
 +
 +<script type="text/plain" title="BlockingQueue.java">
 +public class BlockingQueue < T extends Object > {
 +
 +   private Queue<T> queue = new LinkedList<T>();
 +   private int capacity;
 +
 +   public BlockingQueue(int capacity) {
 +      this.capacity = capacity;
 +   }
 +
 +   public synchronized void put(T element) {
 +      while (queue.size() == capacity) {
 +         wait();
 +      }
 +
 +      queue.add(element);
 +      notify(); // notifyAll() for multiple producer/consumer threads
 +   }
 +
 +   public synchronized T take() {
 +      while (queue.isEmpty()) {
 +         wait();
 +      }
 +
 +      T item = queue.remove();
 +      notify(); // notifyAll() for multiple producer/consumer threads
 +      return item;
 +   }
 +   
 +   public int size() {
 +      return queue.size();
 +   }
 +}
 +</script>
 +</div>
 +</HTML>
 +
 +===== synchronized-Block =====
 +<HTML>
 +
 +<div class="java-online" style="height: 60vh; width: 90%" data-java-online="{'withBottomPanel': false, 'id': 'ThreadTest2'}">
 +
 +<script type="text/plain" title="ThreadTest1.java">
 +
 +Counter counter = new Counter();
 +
 +for (int i = 0; i < 3; i++) {
 +   Incrementer inc = new Incrementer(counter, 1e6, i);
 +   Thread t = new Thread(inc);
 +   t.setName("Thread " + i);
 +   t.start();
 +}
 +
 +class Counter {
 +   long counter = 0;
 +}
 +
 +
 +class Incrementer implements Runnable {
 +   
 +   Counter counter;
 +   long increment = 0;
 +   int index;
 +
 +   Incrementer(Counter counter, long increment, int index) {
 +      this.increment = increment;
 +      this.counter = counter;
 +      this.index = index;
 +   }
 +
 +   public void run() {
 +      for (long i = 0; i < increment; i++) {
 +         if(i % (increment/10) == 0) println("Thread " + index + ": " + i);
 +         
 +         synchronized (counter) {
 +            long i = counter.counter;
 +            i++;
 +            counter.counter = i;
 +         }
 +         
 +      }
 +      println("Thread " + index + " done. Counter: " + counter.counter);
 +   }
 +
 +}
 +</script>
 +
 +</div>
 +
 +</HTML>
 +
 +
api/documentation/threads/start.1730123175.txt.gz · Zuletzt geändert: 2024/10/28 13:46 von martin