Callable oder Runnable mit FutureTask ummanteln
Die Klasse java.util.concurrent.FutureTask implementiert Future, Runnable und RunnableFuture und wird intern von der Java-Bibliothek verwendet, wenn wir mit submit(…) etwas beim ExecutorService absetzen. Auch wir können den Typ direkt als Wrapper um ein Callable oder Runnable nutzen, denn es gibt praktische Callback-Methoden, die wir überschreiben können, etwa done(), wenn eine Berechung fertig ist.
Dazu ein Beispiel: Ein Callable liefert den Namen des Benutzers. Ein FutureTask legt sich um dieses Callable und bekommt mit, wann das Callable fertig ist und modifiziert dann den Benutzernamen und gibt weiterhin eine Meldung aus.
Callable username = () -> System.getProperty( "user.name" );
FutureTask wrappedUsername = new FutureTask( username ) {
@Override
protected void done() {
try {
System.out.printf( "done: isDone=%s, isCancelled=%s%n", isDone(), isCancelled() );
System.out.println( "done: get=" + get() );
}
catch ( InterruptedException | ExecutionException e ) { /* Ignore */ }
}
@Override
protected void set( String v ) {
System.out.println( "set: " + v );
super.set( v.toUpperCase() );
}
};
ExecutorService scheduler = Executors.newCachedThreadPool();
scheduler.submit( wrappedUsername );
System.out.println( "main: " + wrappedUsername.get() );
scheduler.shutdown();
Wichtig in der Nutzung ist, nicht die Rückgabe vom submit(…) auszuwerten, was wir normalerweise machen, sondern das übergebene FutureTask zu erfragen.
Die Ausgaben vom Programm sind oft ein wenig durcheinander:
set: Christian
done: isDone=true, isCancelled=false
done: get=CHRISTIAN
main: CHRISTIAN
Die Reihenfolge in den Aufrufen ist immer so: Der FutureTask stellt die Fertigstellung vom Callable fest und ruft set(…) auf. Anschließend done().