homedukeOOP mit Java: Synchronisation von Threads Prof. Dr. Uwe Schmidt FH Wedel

Synchronisation von Threads

weiter

weiter

Synchronisation

Zugriff
auf gemeinsame Resourcen
in Java: Zugriff auf Objekte
weiter
Beispiel
class Name {
  String firstname;
  String surname;
 
  public
  void setName(String firstname,
               String surname) {
    this.firstname = firstname;
    this.surname   = surname;
  }
 
  public
  String getName() {
    return
      firstname + " " + surname;
  }
}
weiter
merke
Problem
 
Name n;
 
//-------------
 
// thread 1:
  ...
  n.setName("Charlie","Chaplin");
  ...
  n.setName("Jerry","Lewis");
  ...
 
//-------------
 
// thread 2:
 
  ... n.getName() ...
merke
mögliche Resultate für n.getName():
 
"Charlie Chaplin"
"Jerry Lewis"
"Jerry Chaplin"
"Charlie Lewis"
Ursache
this.firstname = firstname;
this.surname   = surname;
und
return
  firstname + " " + surname;
 
sind unterbrechbare (teilbare) Operationen
weiter

weiter

Lösung

Monitore
Synchronisation mit Monitoren
 
Jedes Objekt wird zu einem Monitor gemacht.
 
class SyncName {
  String firstname;
  String surname;
 
  public
  synchronized
  void setName(String firstname,
               String surname) {
      this.firstname = firstname;
      this.surname   = surname;
  }
 
  public
  synchronized
  String getName() {
      return
          firstname + " " + surname;
  }
}
weiter
Semantik
Naiver Implementierungsversuch
merke
NUR zur Veranschaulichung der Bedeutung von synchronized, die Semantik ist in Wirklichkeit aber komplexer.
weiter
 
class SyncName {
    String firstname;
    String surname;
 
    boolean busy = false;
 
  public
  void setName(String firstname,
               String surname) {
      while (busy) {}
      busy = true;
 
      this.firstname = firstname;
      this.surname   = surname;
 
      busy = false;
  }
 
  public
  String getName() {
      String res;
      while (busy) {}
      busy = true;
 
      res = firstname + " " + surname;
 
      busy = false;
 
      return
          res;
  }
}
merke
setName und getName sind unteilbare Operationen geworden
merke
Solange eine synchronized-Routine ausgeführt wird, kann keine andere synchronized-Routine auf diesem Objekt ausgeführt werden.
weiter
Syntax
synchronized-Modifier
für Methoden
 
    ...
    synchronized
    Type method(...) {
          // der gesamte Rumpf ist synchronisiert
          // mit this
      ...
    }
synchronized-Block
 
    ...
    synchronized ( e ) {
          // synchronized statement
          // dieser Block wird synchronisiert
          // mit dem Objekt, das durch e referenziert wird
      ...
    }
Beispiel
weiter

weiter

Synchronisation mit wait und notify

Problem
Zugang zum Monitor abhängig vom Zustand des Monitors
Beispiel
Verbraucher-Prozess möchte Daten aus einem Puffer abholen, der Puffer ist aber noch nicht vom Erzeuger-Prozess gefüllt
Lösung:
wait() gibt Monitor für andere Prozesse frei
notify() stößt andere Prozesse, die ein wait() ausgeführt haben, wieder an.
 
wait() und notify() sind in Object definiert, also für alle Objekte vorhanden.
Klassisches Beispiel
Erzeuger-Verbraucher mit Puffer als gemeinsamer Resource
 
class Buffer {
 
  // the two buffer variables
  private
  boolean empty = true;
 
  private
  int     value;
 
  //--------------------
  // the producer routine
 
  public
  synchronized
  void put(int v) {
 
    // using if in this context is an error
    // example not yet complete !!!
 
    if ( ! empty ) {
      // producer must wait until buffer empty
      try {
        wait();
      }
      catch ( InterruptedException e ) { }
    }
 
    // here the buffer is empty, fill it
    value = v;
    empty = false;
 
    // notify waiting consumer
    notify();
  }
 
  //--------------------
  // the consumer routine
 
  public
  synchronized
  int get() {
    int v;
 
    // using if in this context is an error
    // example not yet complete !!!
 
    if ( empty ) {
      // consumer must wait until buffer is full
      try {
        wait();
      }
      catch ( InterruptedException e ) { }
    }
 
    // here the buffer is full, empty it
    v     = value;
    empty = true;
 
    // notify waiting producer
    notify();
 
    return v;
  }
}
Beispiel
weiter

weiter

Verklemmungen, deadlocks

Problem
Zwei threads und zwei Resourcen,
 
jeder thread befindet sich in einem kritischen Abschnitt einer Resource und möchte auf die jeweils andere Resource zugreifen
Beispiel
class Deadlock {
 
    Object lock1 = new SomeClass();
    Object lock2 = new SomeClass();
 
    public void foo() {
        synchronized (lock1) {
            synchronized (lock2) {
                // ...
            }
        }
    }
 
    public void bar() {
        synchronized (lock2) {
            synchronized (lock1) {
                // ...
            }
        }
    }
}
weiter
merke
Die Erkennung von deadlocks ist nicht immmer so einfach!!!
2. Beispiel
Zyklische Struktur mit geschachtelten Monitoraufrufen
 
class X {
    Y y1;
 
    public
    synchronized
    void setY1(Y y) {
        y1 = y;
    }
 
    public
    synchronized
    void foo() {
        y1.bar();
    }
 
    public
    synchronized
    void bar() {
        // ...
    }
}
 
class Y {
    X x1;
 
    public
    synchronized
    void setX1(X x) {
        x1 = x;
    }
 
    public
    synchronized
    void foo() {
        x1.bar();
    }
 
    public
    synchronized
    void bar() {
        // ...
    }
}
 
public
class Deadlock2 {
 
    X x;
    Y y;
 
    public Deadlock2() {
        x = new X();
        y = new Y();
 
        x.setY1(y);
        y.setX1(x);
 
        t1 = new MyThread(x,y);
        t2 = new MyThread(x,y);
 
        t1.start()// --> x.foo()
        t2.start()// --> y.foo()
    }
}
merke
Geschachtelte Monitor-Aufrufe für verschiedene Objekte bergen immer die Gefahr von Verklemmungen.
merke
Die Deadlock-Freiheit kann nicht durch Testen gezeigt werden.
merke
Die Deadlock-Freiheit kann nur durch systematische Programm-Konstruktion erreicht werden.
merke
nie synchronized-Methoden von anderen Objekten aus synchronized-Methoden aufrufen. Deadlocks erfordern immer mindestens zwei Threads. Der Grund hierfür liegt darin, dass ein Thread, der in einen Monitor eingetreten ist, wieder synchronized Blöcke dieses Monitors betreten darf (Java Language Specification: Chapter 17 Threads and Locks: 17.5 Rules about Locks).
Beispiel
Die beiden folgenden Programmfragmente erzeugen also, wie in einer früheren Version dieser Seite fälschlicherweise dargestellt, keinen Deadlock, da hier der gleicheThread mehrfach einen Monitor betritt. Dieses ist explizit erlaubt.
 
class X {
  synchronized void f() {
    // no deadlock: same monitor
    f();
  }
}
oder
 
class X {
  void f() {
    synchronized (this) {
      synchronized (this) {
        // no deadlock: same monitor
      }
    }
  }
}
weiter

weiter

Semaphore in Java

Semaphore
sind in eine Richtung beschränkte Zähler mit Synchronisation der zählenden Prozesse.
Beispiel
public
class CountingSemaphore {
    private int count;
 
    public
    CountingSemaphore(int initialCount) {
        count = initialCount;
    }
 
    public
    synchronized
    void P() {
                // if would be an error !!!
        while ( count <= 0 ) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        --count;
    }
 
    public
    synchronized
    void V() {
        ++count;
        notify();
    }
}
merke
zyklische Abhängigkeiten in den Monitoren
merke
wait Aufruf in einer while-Scheife,
sonst Gefahr von Inkonsistenzen
spin locks
merke
defensivere Strategie:
notify wird durch notifyAll ersetzt
Probleme
mit stop() und destroy()
 
Abbrechen eines threads während dieser sich mit der Ausführung in einen Monitor befindet
Beispiel
class X {
    // ...
 
    synchronized void f() {
        // ...
 
        Thread.currentThread().stop();
    }
}
Javadoc
merke
stop() ist deprecated (missbilligt) ab JDK 1.2
weiter

Letzte Änderung: 14.02.2012
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel