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 jeweiligeType
-object
eines jeden Threads dieselbe Seriennummer1
zurückliefern (notwendige Bedingung).
Die Klasse pp.Type
soll nicht verändert werden.