Nebenläufigkeit in Java

Concurrency in Java

Dozent:

Prof. Dr. Michael Eichberg

Kontakt:

michael.eichberg@dhbw-mannheim.de

Version:

2024-05-09

Folien:
Fehler auf Folien melden:

Nebenläufigkeit

Ein gutes Verständnis von nebenläufiger Programmierung ist für die Entwicklung von verteilten Anwendungen unerlässlich, da Server immer mehrere Anfragen gleichzeitig bearbeiten.

Prozesse vs. Threads

Prozesse vs. Threads
Prozesse vs. Threads
Prozesse vs. Threads

Kommunikation und Synchronisation mit Hilfe von Monitoren

Ein Monitor ist ein Objekt, bei dem die Methoden im wechselseitigen Ausschluss (engl. mutual exclusion) ausgeführt werden.

Monitor

Bedingungs-Synchronisation

  • drückt eine Bedingung für die Reihenfolge der Ausführung von Operationen aus.

  • z. B. können Daten erst dann aus einem Puffer entfernt werden, wenn Daten in den Puffer eingegeben wurden.

  • Java unterstützt pro Monitor nur eine (anonyme) Bedingungs-Variable, mit den klassischen Methoden wait und notify bzw. notifyAll.

Monitore sind nur ein Modell (Alternativen: Semaphores, Message Passing), das die Kommunikation und Synchronisation von Threads ermöglicht. Es ist das Standardmodell in Java und wird von der Java Virtual Machine (JVM) unterstützt.

Kommunikation zwischen Threads mit Hilfe von Monitoren

Nebenfäufigkeit in Java

java.lang.Thread

Inter-Thread-Kommunikation bzw. Koordination

Java Thread States

Java Thread States

synchronized-Methoden und synchronized-Blöcke

Beispiel: Synchronisierte Methode

public class SynchronizedCounter {

  private int count = 0;

  public synchronized void increment() {
    count++;
  }

  public synchronized int getCount() {
    return count;
  }
}
  public class SharedLong {

    private long theData; // reading and writing longs is not atomic

    public SharedLong(long initialValue) {
      theData = initialValue;
    }

    public synchronized long read() { return theData; }

    public synchronized void write(long newValue) { theData = newValue; }

    public synchronized void incrementBy(long by) {
      theData = theData + by;
    }
  }

  SharedLong myData = new SharedLong(42);
public class SynchronizedCounter {

  private int count = 0;

  public void increment() {
    synchronized(this) {
      count++;
    }
  }

  public int getCount() {
    synchronized(this) {
      return count;
    }
  }
}

Dies liegt daran, dass es nicht möglich ist, die mit einem bestimmten Objekt verbundene Synchronisation zu verstehen, indem man sich nur das Objekt selbst ansieht. Andere Objekte können bgzl. des Objekts eine synchronized-Block verwenden.

Komplexe Rückgabewerte

public class SharedCoordinate {

  private int x, y;

  public SharedCoordinate(int initX, int initY) {
    this.x = initX; this.y = initY;
  }

  public synchronized void write(int newX, int newY) {
    this.x = newX; this.y = newY;
  }

  /* ⚠️ */ public /* synchronized irrelevant */ int readX() { return x; } /* ⚠️ */
  /* ⚠️ */ public /* synchronized irrelevant */ int readY() { return y; } /* ⚠️ */

  public synchronized SharedCoordinate read() {
    return new SharedCoordinate(x, y);
  }
}

Die beiden Methoden: readX und readY sind nicht synchronisiert, da das Lesen von int-Werten atomar ist. Allerdings erlauben sie das Auslesen eines inkonsistenten Zustands! Es ist denkbar, dass direkt nach einem readX der entsprechende Thread unterbrochen wird und ein anderer Thread die Werte von x und y verändert. Wird dann der ursprüngliche Thread fortgesetzt, und ruft readY auf, so erhält er den neuen Wert von y und hat somit ein paar x, y vorliegen, dass in dieser Form nie existiert hat.

Ein konsistenter Zustand kann nur durch die Methode read ermittelt werden, die die Werte von x und y in einem Schritt ausliest und als Paar zurückgibt.

Kann sichergestellt werden, dass ein auslesender Thread die Instanz in einem synchronized Block benennt, dann kann die Auslesung eines konsistenten Zustands auch bei mehreren Methodenaufrufen hintereinander sichergestellt werden.

SharedCoordinate point = new SharedCoordinate(0,0);
synchronized (point1) {
  var x = point1.readX();
  var y = point1.readY();
}
// do something with x and y

Diese Lösung muss jedoch als sehr kritisch betrachtet werden, da die Wahrscheinlichkeit von Programmierfehlern sehr hoch ist und es dann entweder zur Race Conditions oder zu Deadlocks kommen kann.

Bedingte Synchronisation

Zum Zwecke der bedingten Synchronisation können in Java die Methoden wait, notify und notifyAll verwendet werden. Diese Methoden erlauben es auf bestimmte Bedingungen zu warten und andere Threads zu benachrichtigen, wenn sich die Bedingung geändert hat.

  • Diese Methoden können nur innerhalb von Methoden verwendet werden, die die Objektsperre halten; andernfalls wird eine IllegalMonitorStateException ausgelöst.

  • Die wait-Methode blockiert immer den aufrufenden Thread und gibt die mit dem Objekt verbundene Sperre frei.

  • Die notify-Methode weckt einen wartenden Thread auf. Welcher Thread aufgeweckt wird, ist nicht spezifiziert.

    notify gibt die Sperre nicht frei; daher muss der aufgeweckte Thread warten, bis er die Sperre erhalten kann, bevor er fortfahren kann.

  • Um alle wartenden Threads aufzuwecken, muss die Methode notifyAll verwendet werden.

    Warten die Threads aufgrund unterschiedlicher Bedingungen, so ist immer notifyAll zu verwenden.

  • Wenn kein Thread wartet, dann haben notify und notifyAll keine Wirkung.

Beispiel: Bedingte Synchronisation mit Condition Variables

Ein BoundedBuffer hat z. B. traditionell zwei Bedingungsvariablen: BufferNotFull und BufferNotEmpty.

Wenn ein Thread auf eine Bedingung wartet, kann kein anderer Thread auf die andere Bedingung warten.

Mit den bisher vorgestellten Primitiven ist eine direkte Modellierung dieses Szenarios so nicht möglich. Stattdessen müssen immer alle Threads aufgeweckt werden, um sicherzustellen, dass auch der intendierte Thread aufgeweckt wird. Deswegen ist auch das Überprüfen der Bedingung in einer Schleife notwendig.

  public class BoundedBuffer {
    private final int buffer[];
    private int first;
    private int last;
    private int numberInBuffer = 0;
    private final int size;

    public BoundedBuffer(int length) {
      size = length;
      buffer = new int[size];
      last = 0;
      first = 0;
    };
    ...
  }
  public synchronized void put(int item) throws InterruptedException {
    while (numberInBuffer == size)
      wait();
    last = (last + 1) % size;
    numberInBuffer++;
    buffer[last] = item;
    notifyAll();
  };
  public synchronized int get() throws InterruptedException {
    while (numberInBuffer == 0)
      wait();
    first = (first + 1) % size;
    numberInBuffer--;
    notifyAll();
    return buffer[first];
  }
}

Fehlersituation, die bei der Verwendung von notify (statt notifyAll) auftreten könnte.

BoundedBuffer bb = new BoundedBuffer(1);
Thread g1,g2 = new Thread(() => { bb.get(); } );
Thread p1,p2 = new Thread(() => { bb.put(new Object()); } );
g1.start(); g2.start(); p1.start(); p2.start();

Aktionen

(Änderung des) Zustand(s) des Buffers

Auf die Sperre (Lock) wartend

An der Bedingung wartend

1

g1:bb.get()
g2:bb.get(), p1:bb.put(), p2:bb.put()

empty

{g2,p1,p2}

{g1}

2

g2:bb.get()

empty

{p1,p2}

{g1,g2}

3

p1:bb.put()

empty → not empty

{p2,g1}

{g2}

4

p2:bb.put()

not empty

{g1}

{g2,p2}

5

g1:bb.get()

not empty → empty

{g2}

{p2}

6

g2:bb.get()

empty

{g2,p2}

In Schritt 5 wurde von der VM - aufgrund des Aufrufs von notify durch g1 - der Thread g2 aufgeweckt - anstatt des Threads p2. Der aufgeweckte Thread g2 prüft die Bedingung (Schritt 6) und stellt fest, dass der Buffer leer ist. Er geht wieder in den Wartezustand. Jetzt warten sowohl ein Thread, der ein Wert schreiben möchte als auch ein Thread, der einen Wert lesen möchte.

Fortgeschrittene Synchronisationsmechanismen, -primitive und -konzepte.

Java API für nebenläufige Programmierung

java.util.concurrent:

Bietet verschiedene Klassen zur Unterstützung gängiger nebenläufiger Programmierparadigmen, z. B. Unterstützung für BoundedBuffers oder Thread-Pools.

java.util.concurrent.atomic:

Bietet Unterstützung für sperrfreie (lock-free), thread-sichere Programmierung auf einfachen Variablen — wie zum Beispiel atomaren Integern — an.

java.util.concurrent.locks:

Bietet verschiedene Sperralgorithmen an, die die Java-Sprachmechanismen ergänzen, z. B. Schreib-Lese-Sperren und Bedingungsvariablen. Dies ermöglicht zum Beispiel: Hand-over-Hand oder Chain Locking.

Beispiel: Bedingte Synchronisation mit ReentrantLocks.

Ein BoundedBuffer hat z. B. traditionell zwei Bedingungsvariablen: BufferNotFull und BufferNotEmpty.

public class BoundedBuffer<T> {

  private final T buffer[];
  private int first;
  private int last;
  private int numberInBuffer;
  private final int size;


  private final Lock lock = new ReentrantLock();
  private final Condition notFull = lock.newCondition();
  private final Condition notEmpty = lock.newCondition();
  public BoundedBuffer(int length) { /* Normaler Constructor. */
    size = length;
    buffer = (T[]) new Object[size];
    last = 0;
    first = 0;
    numberInBuffer = 0;
  }
  public void put(T item) throws InterruptedException {
    lock.lock();
    try {

      while (numberInBuffer == size) { notFull.await(); }
      last = (last + 1) % size;
      numberInBuffer++;
      buffer[last] = item;
      notEmpty.signal();

    } finally {
      lock.unlock();
    }
  }
  public T get() ... {
    lock.lock();
    try {

      while (numberInBuffer == 0) { notEmpty.await(); }
      first = (first + 1) % size;
      numberInBuffer--;
      notFull.signal();
      return buffer[first];

    } finally {
      lock.unlock();
    }
  }
}

Thread Prioritäten

Best Practices

Ressourcen immer in der gleichen Reihenfolge sperren

Thread Safety

Thread Safety - Voraussetzung

Damit eine Klasse thread-sicher ist, muss sie sich in einer single-threaded Umgebung korrekt verhalten.

D. h. wenn eine Klasse korrekt implementiert ist, dann sollte keine Abfolge von Operationen (Lesen oder Schreiben von öffentlichen Feldern und Aufrufen von öffentlichen Methoden) auf Objekten dieser Klasse in der Lage sein:

  • das Objekt in einen ungültigen Zustand versetzen,

  • das Objekt in einem ungültigen Zustand zu beobachten oder

  • eine der Invarianten, Vorbedingungen oder Nachbedingungen der Klasse verletzen.

Die Klasse muss das korrekte Verhalten auch dann aufweisen, wenn auf sie von mehreren Threads aus zugegriffen wird.

  • Unabhängig vom Scheduling oder der Verschachtelung der Ausführung dieser Threads durch die Laufzeitumgebung,

  • Ohne zusätzliche Synchronisierung auf Seiten des aufrufenden Codes.

Dies hat zur Folge, dass Operationen auf einem thread-sicheren Objekt für alle Threads so erscheinen als ob die Operationen in einer festen, global konsistenten Reihenfolge erfolgen würden.

Thread Safety Level

Immutable Unveränderlich:

Die Objekt sind konstant und können nicht geändert werden.

Thread-sicher:

Die Objekte sind veränderbar, unterstützen aber nebenläufigen Zugriff, da die Methoden entsprechend synchronisiert sind.

Bedingt Thread-sicher:

All solche Objekte bei denen jede einzelne Operation thread-sicher ist, aber bestimmte Sequenzen von Operationen eine externe Synchronisierung erfordern können.

Thread-kompatibel:

Alle Objekte die keinerlei Synchronisierung aufweisen. Der Aufrufer kann die Synchronisierung jedoch ggf. extern übernehmen.

Thread-hostile Thread-schädlich:

Objekte, die nicht thread-sicher sind und auch nicht thread-sicher gemacht werden können, da sie zum Beispiel globalen Zustand manipulieren.

Übung

Virtueller Puffer

Implementieren Sie einen virtuellen Puffer, der Tasks (Instanzen von java.lang.Runable) entgegennimmt und nach einer bestimmten Zeit ausführt. Der Puffer darf währenddessen nicht blockieren bzw. gesperrt sein.

Nutzen Sie ggf. virtuelle Threads, um auf ein explizites Puffern zu verzichten. Ein virtueller Thread kann zum Beispiel mit: Thread.ofVirtual() erzeugt werden. Danach kann an die Methode start ein Runnable Objekt übergeben werden.

Verzögern Sie die Ausführung (Thread.sleep()) im Schnitt um 100ms mit einer Standardabweichung von 20ms. (Nutzen Sie Random.nextGaussian(mean,stddev))

Starten Sie 100 000 virtuelle Threads. Wie lange dauert die Ausführung? Wie lange dauert die Ausführung bei 100 000 platform (native) Threads.

Nutzen Sie ggf. die Vorlage.

MTAwMDAw:BtafMpmiXECvjIt0lETpMLZafucxBnCEiNZieDuba7Q=:HLLZsVWiiyroL61H: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
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class VirtualBuffer {

  private final Random random = new Random();

  private Thread runDelayed(int id, Runnable task) {
    // TODO
  }

  public static void main(String[] args) throws Exception {
    var start = System.nanoTime();
    VirtualBuffer buffer = new VirtualBuffer();
    List<Thread> threads = new ArrayList<>();
    for (int i = 0; i < 100000; i++) {
      final var no = i;
      var thread = buffer.runDelayed(
          i,
          () -> System.out.println("i'm no.: " + no));
      threads.add(thread);
    }
    System.out.println("finished starting all threads");
    for (Thread thread : threads) {
      thread.join();
    }
    var runtime = (System.nanoTime() - start)/1_000_000;
    System.out.println(
      "all threads finished after: " + runtime + "ms"
    );
  }
}

Übung

Thread-sichere Programmierung

Implementieren Sie eine Klasse ThreadsafeArray zum Speichern von nicht-null Objekten (java.lang.Object) an ausgewählten Indizes — vergleichbar mit einem normalen Array. Im Vergleich zu einem normalen Array sollen die Aufrufer jedoch ggf. blockiert werden, wenn die Zelle belegt ist. Die Klasse soll folgende Methoden bereitstellen:

get(int index):

Liefert den Wert an der Position index zurück. Der aufrufende Thread wird ggf. blockiert, bis ein Wert an der Position index gespeichert wurde. (Die get-Methode entfernt den Wert nicht aus dem Array.)

set(int index, Object value):

Speichert den Wert value an der Position index. Falls an der Position index bereits ein Wert gespeichert wurde, wird der aufrufende Thread blockiert, bis der Wert an der Position index gelöscht wurde.

delete(int index):

Löscht ggf. den Wert an der Position index wenn ein Wert vorhanden ist. Andernfalls wird der Thread blockiert, bis es einen Wert gibt, der gelöscht werden kann.

  1. Implementieren Sie die Klasse ThreadsafeArray nur unter Verwendung der Standardprimitive: synchronized, wait, notify und notifyAll. Nutzen Sie die Vorlage.

  2. Können Sie sowohl notify als auch notifyAll verwenden?

  3. Implementieren Sie die Klasse ThreadsafeArray unter Verwendung von ReentrantLocks und Conditions. Nutzen Sie die Vorlage.

  4. Welche Vorteile hat die Verwendung von ReentrantLocks?

MTAwMDAw:yGBcJXAINs+auQxBdktFfTDkeK8Q4lQkyUMPb4v8Ckk=:S84tfdIDgs9uH6FQ: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

Sie können sich die Klasse ThreadsafeArray auch als ein Array von BoundedBuffers mit der Größe 1 vorstellen.

public class ThreadsafeArray {

  private final Object[] array;

  public ThreadsafeArray(int size) {
    this.array = new Object[size];
  }

  // Methodensignaturen ggf. vervollständigen
  // und Implementierungen ergänzen
  Object get(int index)
  void set(int index, Object value)
  void remove(int index)

  public static void main(String[] args) throws Exception {
    final var ARRAY_SIZE = 2;
    final var SLEEP_TIME = 1; // ms
    var array = new ThreadsafeArray(ARRAY_SIZE);
    for (int i = 0; i < ARRAY_SIZE; i++) {
      final var threadId = i;

      final var readerThreadName = "Reader";
      var t2 = new Thread(() -> {
        while (true) {
          int j = (int) (Math.random() * ARRAY_SIZE);
          try {
            out.println(readerThreadName + "[" + j + "]" );
            var o = array.get(j);
            out.println(readerThreadName +
                "[" + j + "] ⇒ #" + o.hashCode());
            Thread.sleep(SLEEP_TIME);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }, readerThreadName);
      t2.start();

      // One Thread for each slot that will eventually
      // write some content
      final var writerThreadName = "Writer[" + threadId + "]";
      var t1 = new Thread(() -> {
        while (true) {
          try {
            var o = new Object();
            out.println(writerThreadName + " = #" + o.hashCode());
            array.set(threadId, o);
            out.println(writerThreadName + " done");
            Thread.sleep(SLEEP_TIME);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }, writerThreadName);
      t1.start();

      // One Thread for each slot that will eventually
      // delete the content
      final var deleterThreadName = "Delete[" + threadId + "]";
      var t3 = new Thread(() -> {
        while (true) {
          try {
            out.println(deleterThreadName);
            array.delete(threadId);
            Thread.sleep(SLEEP_TIME);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }, deleterThreadName);
      t3.start();
    }
  }
}