Für das Szenario, das Sie beschreiben, kann ich die Verwendung von Instanzen der Klasse java.util.concurrent.CyclicBarrier
empfehlen - sehen Sie das folgende einfache Beispiel eines Threads, der eine Berechnung asynchron ausführt (hier reduziert auf die Einstellung eines "Prozentsatzes getan" -Wertes zuerst auf 50 und dann) zu 100) und erlauben, dass der "Prozentsatz fertig" -Wert vom Hauptthread abgerufen wird.
Hier ist der SUT-Klasse:
public class AsyncProcess implements Runnable {
private int percentageDone = 0;
public int getPercentageDone() { return percentageDone; }
public void doFirstHalf() { percentageDone = 50; }
public void doSecondHalf() { percentageDone = 100; }
public void run() {
doFirstHalf();
doSecondHalf();
}
}
Für Unit-Tests dieser, ich benutze JUnit 4, mit Mockito Spione die beiden Berechnungsmethoden meines SUT abzufangen (alternativ können Sie Ihre Lieblings AOP Framework verwenden).
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doAnswer;
import java.util.concurrent.CyclicBarrier;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class AsyncProcessTest {
@Test
public void testExecute() throws Exception {
final CyclicBarrier firstHalfStarted = new CyclicBarrier(2);
final CyclicBarrier firstHalfFinished = new CyclicBarrier(2);
final CyclicBarrier secondHalfStarted = new CyclicBarrier(2);
final CyclicBarrier secondHalfFinished = new CyclicBarrier(2);
AsyncProcess process = Mockito.spy(new AsyncProcess());
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) throws Throwable {
firstHalfStarted.await();
invocation.callRealMethod();
firstHalfFinished.await();
return null;
}
}).when(process).doFirstHalf();
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) throws Throwable {
secondHalfStarted.await();
invocation.callRealMethod();
secondHalfFinished.await();
return null;
}
}).when(process).doSecondHalf();
new Thread(process, "AsyncProcess").start();
assertThat(process.getPercentageDone(), is(0));
firstHalfStarted.await();
firstHalfFinished.await();
assertThat(process.getPercentageDone(), is(50));
secondHalfStarted.await();
secondHalfFinished.await();
assertThat(process.getPercentageDone(), is(100));
}
}
Dieser Ansatz ermöglicht eine sehr feinkörnige Kontrolle des Ausführungsflusses der beteiligten Threads. Es kann leicht zu einem "Mikro-Framework" erweitert werden, indem die CyclicBarrier-Paare eingekapselt und die Interception-Aufrufe sowie die Unterstützung für Timeouts und die korrekte Ausnahmebehandlung extrahiert werden.
Zwei hilfreiche Tools - [Verfügbarkeit] (https://github.com/awaitility/awaitility) und [ConcurrentUnit] (https://github.com/jhalterman/concurrentunit). – Jonathan