pp.02.03-Lock

Gegenseitiger Ausschluss

  • Projekt: pp.02.03-Lock
  • Bearbeitungszeit: 15 Minuten
  • Musterlösung: 15 Minuten
  • Kompatibilität: mindestens Java SE 10

Das Factory-Pattern wird durch eine Klasse umgesetzt, die Instanzen (hier der Klasse pp.Type) durch Aufruf einer Methode (statt eines Konstruktors) liefert. Normalerweise handelt es sich um eine Klassenmethode (static). In diesem Beispiel realisert die Factory-Methode getInstance zusätzlich das Singleton-Muster: Die Factory-Methode erzeugt höchstens eine Instanz und speichert sie in einem Klassenattribut (static instance). Ist bereits eine Instanz vorhanden, wird sie von der Factory-Methode zurückgeliefert.

Um das zu verdeutlichen, merkt sich Type wie oft der Konstruktor verwendet wurde (wieviele Instanzen der Klasse es also gibt). In jedem Objekt von Type ist vermerkt, die wievielte Instanz es gewesen ist. Diese Seriennummer kann durch die Methodee getSerial() abgefragt werden.

Die Klasse Factory hat eine main-Methode, die 100 Threads startet. In jedem Thread wird eine Type-Instanz von der Factory-Methode getInstance() angefordert. Das dauert etwas, da zuerst Type.prepare() aufegrufen werden muss. In dieser Methode wird asynchron etwas getan, was zeitaufwändig ist. Type.prepare() muss jedes mal ausgeführt werden, wenn eine Instanz abgerufen werden soll. Es gibt aber keinen inneren Zusammenhang zum Konstruktor von Type. Type.prepare() ist zudem “threadsicher”.

In jedem Thread gibt es in der lokalen Variable object eine Referenz auf ein Type-Objekt, das über die Factory-Methode getInstance() abgerufen wurde. Eigentlich sollte es sich immer um dasselbe Objekt handeln. Leider stellt die Factory-Methode einen kritischen Abschnitt dar: In jedem Thread müsste also der Aufruf von object.getSerial() denselben Wert 1 zurückliefern. Kommt es aber zum überlappenden Ausführen von getInstance() durch zwei Threads, können zwei Instanzen gebildet werden.

Quellcode von pp.Factory

package pp;

public class Factory {

  private static Type instance;

  public static Type getInstance() {
    Type.prepare();
    if (instance == null) {
      instance = new Type();
    }
    return instance;
  }

  public static void main(String... args) throws InterruptedException {
    var now = System.currentTimeMillis();
    var threads = new Thread[100];
    for (var i = 0; i < 100; i++) {
      threads[i] = new Thread(() -> {
        Type object = Factory.getInstance();
        System.out.println(Thread.currentThread().getName() + ": serial of instance = " + object.getSerial());
      }, String.format("InstanceGrabber-%02d", i));
      threads[i].start();
    }
    for (var i = 0; i < 100; i++) {
      threads[i].join();
    }
    var time = System.currentTimeMillis() - now;
    System.out.println("Dauer: " + time + "ms");
  }
}

Aufgaben

  • Ändern Sie pp.Factory so, dass der kritische Abschnitt geschützt wird. Das Hauptprogramm muss für das jeweilige Type-object eines jeden Threads dieselbe Seriennummer 1 zurückliefern (notwendige Bedingung).

Die Klasse pp.Type soll nicht verändert werden.