Wie kann man in einem Programm mit mehreren Threads auf den Abschluss einer Methodenausführung warten?
Längere Anteile einer Programmausführung werden gemeinhin in eigene Threads ausgelagert, die parallel ausgeführt werden. Da Threads jedoch asynchron arbeiten, ist nicht recht klar, zu welchem Zeitpunkt welcher Thread aktiv ist.
Das Beispiel zeigt dies anhand eines Schreib- und Lesevorgangs in ein und die selbe Datei.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class AufMethodeWarten {
static String file = "test.txt";
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
write();
System.out.println("writing finished");
}
};
t1.start();
Thread t2 = new Thread() {
public void run() {
read();
System.out.println("reading finished");
}
};
t2.start();
}
static void write() {
PrintWriter writer = null;
try {
writer = new PrintWriter(new FileWriter(file), true);
for (int i = 0; i < 10; i++) {
System.out.println("writing " + i);
writer.write(new Integer(i).toString() + "\n");
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
}
}
static void read() {
BufferedReader reader = null;
String zeile;
try {
reader = new BufferedReader(new FileReader(file));
while ((zeile = reader.readLine()) != null) {
System.out.println("reading " + zeile);
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
if (reader != null){
reader.close();
}
} catch (IOException ioe) {
}
}
}
}
Im Beispiel sind neben main()
zwei Methoden deklariert,
die einmal schreibend und einmal lesend auf ein und die selbe Datei
zugreifen. Beide Methoden werden nacheinander in main()
aufgerufen und liefern die folgende Ausgabe:
reading 0 reading 1 writing 0 reading 2 reading 3 reading 4 writing 1 reading 5 writing 2 reading 6 reading 7 reading 8 reading 9 writing 3 writing 4 reading finished writing 5 writing 6 writing 7 writing 8 writing 9 writing finished
Das so nicht vorhersehbare Ergebnis fällt bei fast jedem
Programmstart anders aus, da sowohl der Zeitpunkt als auch die Dauer
der Threadausführung zunächst nicht zu beeinflussen sind.
Erkennbar ist dies an der abwechselnden Ausführung beider
Methoden. Dies hat Vorteile, da auf diese Weise beide
Ausführungsstränge pseudo-gleichzeitig, in Wirklichkeit
jedoch schnell wechselnd ausgeführt werden.
Was jedoch, wenn
beide Threads nacheinander ablaufen sollen, z.B. um den
Schreibvorgang zunächst abzuschließen bevor die Datei
ausgelesen wird?
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class AufMethodeWarten {
static String file = "test.txt";
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
write();
System.out.println("writing finished");
}
};
t1.start();
Thread t2 = new Thread() {
public void run() {
try {
t1.join();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
read();
System.out.println("reading finished");
}
};
t2.start();
}
static void write() {
PrintWriter writer = null;
try {
writer = new PrintWriter(new FileWriter(file), true);
for (int i = 0; i < 10; i++) {
System.out.println("writing " + i);
writer.write(new Integer(i).toString() + "\n");
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
}
}
static void read() {
BufferedReader reader = null;
String zeile;
try {
reader = new BufferedReader(new FileReader(file));
while ((zeile = reader.readLine()) != null) {
System.out.println("reading " + zeile);
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException ioe) {
}
}
}
}
Hierzu bietet die Klasse Thread
die mehrfach
überladene Methode join()
an. Wird sie - wie hier
- ohne Parameter innerhalb des schreibenden Threads auf dem Objekt
des lesenden Threads aufgerufen, so wartet dieser mit seiner
Ausführung, bis der Schreibvorgang abgeschlossen ist. Das
Ergebnis ist die folgende Ausgabe:
writing 0 writing 1 writing 2 writing 3 writing 4 writing 5 writing 6 writing 7 writing 8 writing 9 writing finished reading 0 reading 1 reading 2 reading 3 reading 4 reading 5 reading 6 reading 7 reading 8 reading 9 reading finished
Der Aufruf von join()
muss innerhalb eines
try-catch-Blockes erfolgen, um eventuelle Unterbrechungen
abzufangen.
Neben der parameterlosen Version bietet die Klasse Thread
noch zwei weitere Varianten der Methode: join(long millis)
wartet lediglich millis
Millisekunden bis der Thread
ausgeführt wird. Die zweite Variante besitzt noch einen
zusätzlichen int
-Parameter für eine Angabe
von Nanosekunden: join(long millis, int nanos)
. Die
hier angewandte parameterlose Variante entspricht exakt der
Ausführung von join(0)
.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.