= Multithreading in Java =

Ich habe eine Swing-Applikation, die, nachdem ich immer mehr Funktionen
eingebaut habe, etwas langsam in der Reaktion wurde. Gleichzeitig wusste
ich natürlich, daß so manche Berechnung oder so mancher Datenbank-Zugriff
auch parallel ablaufen könnte. Also habe ich beschlossen, meine ganze
Applikation und die zugrundeliegenden Bibliotheken multithreading-fähig
zu machen. Dazu habe ich mir eine Menge Dinge angelesen und so langsam
die Angst vor der ganzen Sache verloren. Die dabei gewonnenen Erkenntnisse
möchte ich hier notieren.

Einige der von mir gezogenen Schlüsse und Regeln stehen so nirgendwo
geschrieben. Ich bitte also darum, daß der geneigte Leser sich selber
Gedanken macht und Denkfehler bitte direkt ändert oder kommentiert!



== Problemfelder ==

Bei der Implementierung von Multithreading in Java (und übrigens auch in
anderen Sprachen) gilt es, mehrere Probleme zu lösen:



=== Synchronisierung ===

Als erstes müssen alle parallel laufenden Methoden synchronisiert werden.
Das heisst, es muss dafür gesorgt werden, daß sie sich nicht gegenseitig
stören. Die Synchronisation geschieht in der einfachen Variante über
das Schlüsselwort "synchronized" und bei komplexeren Szenarien über
Lock-Objekte. Hat man zwei oder mehr verschiedene Objekte, an denen man
synchronisiert, ergibt sich die Gefahr von Deadlocks - hat man nur eines,
ergibt sich im Zweifel keine echte parallele Abarbeitung, weil die Tasks
ständig in der Warteschlange des einen Objektes stehen. Es gibt "natürliche"
Lock-Objekte wie eine Datenbank-Verbindung oder ein Speicherbereich (Ein
solches natürliches Lock-Objekt ist übrigens auch die GUI, was u.a. ein
Grund dafür ist, daß Swing gar nicht erst multithreading-fähig ist).



=== Aufruf von Threads ===

Statt dem normalen {{Thread.start()}} sollte man einen {{Executor}} benutzen. Das hat
vor allem den Vorteil, daß man über die Auswahl des {{~ExecutorService}} im
Nachhinein noch an der Multithreading-Strategie basteln kann, ohne daß man
das an allen Stellen im Code ändern muss, wo man Threads startet. Wer eine
Webapplikation schreibt, wird z.B. einen {{~ExecutorService}} mögen, der die Anzahl der
erzeugten Threads nach oben hin begrenzt (und die restlichen dann solange in
eine Warteschlange stellt). Wer eine Desktop-Applikation schreibt, wird im
Notfall (beim Auftreten seltsamer Fehler) einen {{Executor}} mögen, der gar keinen
neuen Thread startet, sondern das {{Runnable}} sofort im aktuellen Thread
ausführt. Für Resourcen, die es nur einmal gibt (wenn ich z.B. im Hintergrund
meinen Scanner ansprechen will), gibt es auch einen Executor, der alle
Aufrufe zwar in einem eigenen Thread, aber immer im selben und damit
hintereinander ausführt.



=== Kommunikation zwischen Threads ===

Mit diesem Thema habe ich mich noch nicht so ausführlich beschäftigt, weil
das in meinem Projekt nicht so wichtig war. Es gibt aber auch hierzu eine
ganze Fülle von hilfreichen Klassen in der Java Concurrency API.

In meinem Projekt (und wohl auch in vielen anderen Swing-Applikationen)
erschöpft sich das Thema, indem ein Hintergrund-Thread am Ende seines Lebens
ein Ergebnis hat und dann eine Methode, die dieses Ergebnis benutzt und etwas
damit macht, im Swing Event Dispatcher Thread ausführt. Genau dies übernimmt
am besten der {{~SwingWorker}}.



=== Besonderheiten bei Swing ===

Alle Swing-Komponenten müssen immer im Swing Event Dispatcher Thread
ausgeführt werden. Das gilt natürlich auch für Listener und ähnliches. Jeder
Aufruf, der evtl. aus einem anderen Thread heraus erfolgen könnte, sollte
also ggf. als Event in den Dispatcher eingestellt werden. Alle {{fire*}}-Methoden
müssen daher durch so etwas wie in der folgenden Überladung benutzt gekapselt
werden:

{{{
	public void firePropertyChange(final PropertyChangeEvent e) {
		if (SwingUtilities.isEventDispatchThread()) {
			super.firePropertyChange(e);
		} else {
			Runnable doFirePropertyChange = new Runnable() {
				public void run() {
					firePropertyChange(e);
				}
			};
			SwingUtilities.invokeLater(doFirePropertyChange);
		}
	}
}}}


== Wo werde ich schlauer? ==

Auf jeden Fall sollte man sich erstmal in Ruhe in das Thema einlesen.
Viele vor allem ältere Einführungen zum Thema beschäftigen sich mit
den Java-Grundbefehlen wie '''synchronized''', '''Object.wait()''',
etc. Diese taugen aber zumeist nur für einfachste Anwendungen. Wer ernsthafte
Bibliotheken schreiben will, sollte die sog. High Level Concurrency Klassen
in '''java.util.concurrent''' benutzen. Diese bieten modernere Schnittstellen
und mehr Möglichkeiten.

* http://java.sun.com/javase/6/docs/technotes/guides/concurrency/index.html - Linkseite von Sun zu den Concurrency Utilities
* http://java.sun.com/javase/6/docs/technotes/guides/concurrency/overview.html - Übersicht über das Java Concurrency Framework
* http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html - Java Tutorial zum Thema (Kapitel über High Level Concurrency leider viel zu kurz)

== Konkretes Vorgehen in Einzelschritten ==

Ich habe in meinem SwingingBeans-Projekt eine Bibliothek, die sowohl auf
eine Datenbank zugreift als auch Swing-Wodgets erzeugt. Daneben gibt es
natürlich auch noch diverse Applikations-Methoden, die ganz andere Dinge
treiben, die auch ggf. in einen eigenen Thread gehören. Ziel ist, die
Antwortzeiten der GUI zu optimieren.

So muss ich vorgehen, um meine Bibliothek multithreading-sicher zu machen:



=== Welche Methoden müssen synchronisiert werden? ===

Als erstes sollten alle Klassen identifiziert werden, die theoretisch in
mehreren Threads ablaufen können. Hier fallen insbesondere Swing-Klassen
weg, weil die sowieso immer im Swing Event Thread ausgeführt werden.

Alle public-Methoden sind jetzt Kandidaten für Synchronisation. Sollte
eine solche kein Kandidat hierfür sein (z.B. weil Sie nur einen Delegator
aufruft), dann müssen stattdessen alle von dieser aufgerufenen Methoden
synchronisiert sein.

Eine Methode, die trivial aussieht, ist nur dann nicht zu synchronisieren,
wenn sie entweder auf gar keine Felder der Klasse zugreift oder wenn es nur
ein einziges Feld ist und dieses entweder final oder volatile ist.

Das ist jetzt eine gute Gelegenheit, die API zu überprüfen, ob vielleicht
die eine oder andere public Methode doch ein Fall für package visibility
ist (also ohne public deklariert wird). Auch die Aufteilung der Klassen
in Pakete kann bei dieser Gelegenheit überdacht werden.

Genau nachdenken, wenn eine Methode andere Methoden anderer Klassen aufruft.
Das führt potentiell zu Deadlocks. Insbesondere {{fire*}}-Methoden sind da
gefährlich. Im Zweifelsfall (bei komplexen Listener-Struktoren) sollte man
den Aufruf von Listenern jeweils in
einen eigenen Task packen, damit sie ausgeführt werden, wenn mein Lock wieder
freigegeben wird.


=== Wie implementiert man Synchronisation? ===

Vorab muss man sich fragen, ob man die Synchronisation, die ja auch
ein Stück Performance verschlingt und den Code etwas unübersichtlicher
macht, in die eigentliche Klasse packt oder eine abgeleitete Variante
erzeugt und die nicht-synchronisierte Version bestehen lässt. Hat man
mehrere Klassen, die sich alle auf ein einheitliches Lock-Objekt
zurückführen lassen (wie bei meiner Datenbank-Bobliothek), so fällt die
Ableitungs-Variante eigentlich aus, wenn man nicht wirklich sicherstellen
kann, daß nicht doch einmal irgendwo ein unsynchronisiertes Objekt
erzeugt wird und dann "durchrutscht". Das Performance-Problem kann man
ggf. durch ein Flag beheben, das in lock() abgefragt wird und ggf. auf
die Synchronisation verzichtet.

Die beste Art der Synchronisation ist mit {{~ReentrantLock}} (im Normalfall)
oder {{~ReadWriteLock}} (wenn es sinnvoll ist, mehrere Reader gleichzeitig,
aber nur einen Writer zuzulassen) zu arbeiten.

Triviale Daten (primitive Typen oder einfache Klassen, die nur aus
solchen bestehen) können mit '''volatile''' synchronisiert werden.

Der Aufruf der Synchronisation sollte über zwei eigene Methoden gehen. So
kann die Synchronisation auch alternativ ganz weggelassen werden (durch
überladen dieser Methoden oder ein dort abgefragtes Flag). Ein gelockter
Block sollte in etwa so aussehen:
{{{
	lock();
     try {
		// ...
     } finally {
         unlock();
     }
}}}

Die lock() und unlock()-Methode kann dann z.B. so aussehen:
{{{
	void lock() throws InterruptedException {
		final int lockTimeout = 60000; // in ms, d.h. 60 Sekunden
		final int infoTimeout = 500; // in ms, d.h. 1/2 Sekunde
		long startzeit = System.currentTimeMillis();
		if (!lock.tryLock(lockTimeout, TimeUnit.MILLISECONDS)) {
			throw new DeadLockException("Deadlock nach " + lockTimeout / 1000f
					+ " Sekunden.");
		}
		long endzeit = System.currentTimeMillis();
		if (endzeit - startzeit > infoTimeout) {
			log.info("Synchronisation: Wartezeit auf Lock "
					+ (endzeit - startzeit) / 1000f + " Sekunden.");
		}
	}
	
	void unlock() {
		lock.unlock();
	}
}}}

Man kann diese Funktionalität und noch mehr hübsche Dinge, wie z.B. eine Analyse
der oben gefundenen Deadlocks in eine eigene von {{~ReentrantLock}} abgeleitete Klasse
packen. Ich habe dies gemacht und meine Klasse {{~TimeoutLock}} genannt. Wer Interesse
hat, kann dort gerne mal reinschauen.



=== An welchem Objekt synchronisiert man? ===
     
In den lock/unlock-Methoden kann das Lock-Objekt eines anderen,
übergeordneten Objektes benutzt werden. So ist es z.B. sinnvoll, in mehreren
Klassen, die sich alle mit einer Datenbank beschäftigen, ein Lock-Objekt
pro JDBC-Connection zu benutzen. Die Reduzierung auf weniger Lock-Objekte
(oder nur eines) reduziert auch die Möglichkeit von Deadlocks.



=== Was ist mit Unterbrechungen ===

Grundsätzlich bin ich der Meinung, daß jeder Thread unterbrechbar sein
sollte. Eine entsprechende Abfrage kann z.B. in lock() erfolgen. Das
bedeutet aber, daß man sich überall Gedanken um die dabei erzeugte
{{~InterruptedException}} machen muss. Kann sowieso nicht schaden, mal viel
öfter bei der Benutzung irgendwelcher Resourcen über
<code>try{...}finally{...}</code> nachzudenken.



=== Swing-Schnittstellen finden ===

Ich muss alle Stellen suchen, an denen aus dem Multithreading-Code heraus
Swing-Methoden aufgerufen werden. Insbesondere habe ich alle {{fire*}}-Methoden
herausgesucht und mit obigem Code dafür gesorgt, daß die Listener immer im
EDT (Event Dispatcher Thread) ausgeführt werden.



=== Starten von Threads ===

Im Normalfall übergibt man sein {{Runnable}} einem {{Executor}} und fertig. Will
man jedoch ein Feedback in einer Swing-GUI haben, so empfiehlt sich, als
{{Runnable}} einen {{~SwingWorker}} zu nehmen (diese Klasse implementiert nämlich
{{Runnable}}) und diesen dann seinem {{Executor}} zu übergeben.

==== Starten von Threads im Swing Application Framework ====

Wenn eine {{@Action}}-Methode des "Swing Application Framework" ein {{Task}}-Objekt
zurückgibt, wird diese auch automatisch als eigener Thread gestartet. {{Task}}
ist von {{~SwingWorker}} abgeleitet.

Zum Starten benutzt das SAF einen sog. {{~TaskService}} mit dem Namen "default",
den man im {{~ApplicationContext}} seines Programmes ersetzen kann (nachdem man
den alten gleichnamigen gelöscht hat). Es handelt sich eigentlich nur um
einen Wrapper um ein {{Executor}}-Objekt, man kann also hier einen eigenen
{{Executor}} einbinden, wenn man auf dessen besondere Fähigkeiten angewiesen
ist.



=== Zugriff auf natürlich eingeschränkte Resourcen ===

Ich möchte in meinem Programm einen Scanner bedienen. Der recht lange
Vorgang des Scannens bietet sich natürlich an, um im Hintergrund abzulaufen.
Nun habe ich eine ganze Weile überlegt, wie man das synchronisiert, zumal ich
ja auch schon auf meine Datenbank-Connection hin synchronisiere und grossen
Respekt vor Deadlocks habe. Die richtige Lösung für solch eine natürlich
auf ein Stück begrenzte Ressource ist jedoch eine andere (so wird es übrigens
auch in Swing gemacht). Man erzeugt einen {{~SingleThreadExecutor}} und stellt
dort dann seine Aufgaben als {{Runnable}} ein. Sie werden dann ordentlich
hintereinander ausgeführt, ohne untereinander synchronisiert werden zu müssen.



=== Was ich nicht synchronisieren kann, kann ich vielleicht klonen ===

Wenn ich eine Datenbank-Operation in einem eigenen Thread ausführen möchte,
kann es durch die Synchronisation trotzdem dazu kommen, daß meine Swing-GUI
eine ganze Weile nicht mehr reagiert, weil sie ja auf das gleiche
Datenbank-Objekt zugreift. Deshalb kann es sinnvoll sein, meinen
Datenbank-Zugriff komplett so zu klonen, daß ich eine zweite
Datenbank-Abfrage habe, die jedoch eine andere Connection benutzt.



== Sonstiges / Weitere Fragen ==

=== Deadlock-Erkennung ===

Meine selbstimplementierten Lock-Objekte erkennen einen Deadlock selber,
wenn sie länger als eine bestimmte Timeout-Zeit keinen Zugriff erhalten
und lösen dann eine entsprechende Exception aus (statt sich wortlos
aufzuhängen).

=== Progress Indicator ===

Es wäre für manche Aufgaben schön, wenn man eine Anzeige über den
Fortgang des Prozesses bekommen könnte.

-- ThomasBayen

[{Tag Java}]