Hier ist mein Ansatz. Bei diesem Test geht es nicht um Deadlocks, sondern um die Konsistenz. Ich teste eine Methode mit einem synchronisierten Block, mit Code, der in etwa wie folgt aussieht:
synchronized(this) {
int size = myList.size();
// do something that needs "size" to be correct,
// but which will change the size at the end.
...
}
Es ist schwierig, ein Szenario zu entwerfen, das zuverlässig zu einem Fadenkonflikt führt, aber ich habe Folgendes getan.
Zunächst erstellte mein Unit-Test 50 Threads, startete sie alle gleichzeitig und ließ sie alle meine Methode aufrufen. Ich verwende ein CountDown Latch, um sie alle gleichzeitig zu starten:
CountDownLatch latch = new CountDownLatch(1);
for (int i=0; i<50; ++i) {
Runnable runner = new Runnable() {
latch.await(); // actually, surround this with try/catch InterruptedException
testMethod();
}
new Thread(runner, "Test Thread " +ii).start(); // I always name my threads.
}
// all threads are now waiting on the latch.
latch.countDown(); // release the latch
// all threads are now running the test method at the same time.
Dies kann zu einem Konflikt führen, muss es aber nicht. Meine testMethod() sollte in der Lage sein, eine Ausnahme zu erzeugen, wenn ein Konflikt auftritt. Aber wir können noch nicht sicher sein, dass dies zu einem Konflikt führen wird. Wir wissen also nicht, ob der Test gültig ist. Hier ist also der Trick: Kommentieren Sie Ihr(e) synchronisierte(s) Schlüsselwort(e) aus und führen Sie den Test durch. Wenn dies zu einem Konflikt führt, schlägt der Test fehl. Wenn er ohne das Schlüsselwort synchronized fehlschlägt, ist Ihr Test gültig.
Das habe ich getan, und mein Test ist nicht fehlgeschlagen, also war es (noch) kein gültiger Test. Aber ich konnte zuverlässig einen Fehler erzeugen, indem ich den obigen Code in eine Schleife einfügte und ihn 100 Mal hintereinander ausführte. Ich rufe die Methode also 5000 Mal auf. (Ja, das wird einen langsamen Test ergeben. Machen Sie sich darüber keine Sorgen. Ihre Kunden werden sich daran nicht stören, also sollten Sie es auch nicht.)
Sobald ich diesen Code in eine äußere Schleife einfügte, konnte ich zuverlässig einen Fehler bei etwa der 20sten Iteration der äußeren Schleife erkennen. Jetzt war ich sicher, dass der Test gültig war, und ich stellte die synchronisierten Schlüsselwörter wieder her, um den eigentlichen Test durchzuführen. (Es funktionierte.)
Möglicherweise stellen Sie fest, dass der Test auf einem Rechner gültig ist und auf einem anderen nicht. Wenn der Test auf einem Rechner gültig ist und Ihre Methoden den Test bestehen, dann sind sie vermutlich auf allen Rechnern thread-sicher. Sie sollten jedoch die Gültigkeit auf dem Rechner prüfen, auf dem Ihre nächtlichen Unit-Tests ausgeführt werden.