Ich bin gerade dabei, einige Tests zu schreiben, um die Entwicklung eines meiner Nebenprojekte voranzutreiben, und stoße auf ein sehr seltsames Java-Verhalten:
Object.wait() führt dazu, dass der Hauptausführungsthread zurückkehrt und alle folgenden Ausführungszeilen überspringt, aber nur beim zweiten Mal, wenn er in einer Schleife aufgerufen wird.
Der Grund, warum ich das weiß, ist, weil ich versuche, Tests zu schreiben, ohne die Verwendung von Thread.sleep(), weil ich glaube, dass es im Allgemeinen keine gute Praxis ist, diese in Hauptausführungsthreads einzufügen, insbesondere bei Tests, die später zu extrem lang laufenden Aufgaben werden könnten.
Hier ist mein Test:
@Test
public void testSendReceiveAll() throws Exception {
for (String s : (ArrayList)testFiles) {
((FakeSendFileApi) sendFileApi).setSender(new InetSocketAddress(LOCALHOST,
LOCALPORT)).setIncomingFileName(s + incrAppend());
PendingFile pendingFile = new PendingFile(TEST_PATH + s, new InetSocketAddress(LOCALHOST,
LOCALPORT));
SendAction sendAction = new SendAction(pendingFile);
Thread sendActionThread = new Thread(sendAction);
synchronized (sendAction){
sendActionThread.start();
sendAction.wait(TIMEOUT_MS);
}
File file = new File(s + fileAppend);
assertTrue(file.exists());
assertTrue(file.isFile());
assertTrue(file.canRead());
assertTrue(file.delete());
}
}
Erklärung dessen, was es tut: Iteriere über alle Testdateien und sende und empfange sie alle lokal. Es gibt eine SendAction-Klasse, die im Test instanziiert und ausgeführt wird:
/**
* Versucht, die angegebene Datei an die angegebene InetSocketAddress zu senden.
* Der Dateipfad muss vollständig entweder absolut oder relativ angegeben werden.
*
* @return true, wenn pendingFile erfolgreich vollständig gesendet wurde.
*/
public synchronized void run() {
try {
ServerSocket serverSocket = new ServerSocket(pendingFile.getSender().getPort());
serverSocket.setSoTimeout(socketTimeoutMillis);
// Blockiert, bis eine Verbindung am angegebenen Socket hergestellt wird oder bis TIMEOUT erreicht ist.
Socket socket = serverSocket.accept();
System.out.println("Datei wird gesendet " + pendingFile.getFileName());
OutputStream outputStream = socket.getOutputStream();
sendByteArray(new RandomAccessFile(pendingFile.getFileName(), "r"), outputStream);
serverSocket.close();
notifyAll();
} catch (IOException e) {
System.err.println(e); // TODO Fehlermeldung angemessen protokollieren
}
}
Das Problem: Wenn ich den synchronisierten Block des Tests erreiche, den Thread zum Senden starte und dann auf ein notify von diesem sendAction warte, funktioniert dies beim ersten Mal in der Schleife. Das zweite Mal jedoch besteht der Test einfach und beendet sich beim Aufruf von
sendAction.wait(TIMEOUT_MS);
Dies tritt nur manchmal auf und nicht immer. Ich habe Druckanzeigen gesetzt, um zu sehen, ob ich die Race-Condition ohne Debugging erreichen kann, und es wird die erste Datei gesendet und empfangen, sendet aber nicht immer die zweite Datei. Wenn ich eine println() Anweisung direkt nach dem Aufruf von sendAction.wait(TIMEOUT_MS); einfüge, wird sie nach der zweiten Schleifendurchlauf nie ausgeführt.
Was ist da los???