Parallele Programmierung (3IB)
ExecutorService
für asynchrone Methodenaufrufe
Callable
, Future
, ExecutorService
Executors
-Factory: Fixed und Cached Threadpoolsshutdown()
)ScheduledExecutorService
(u.a. schedule(...)
)Runnable
und Callable
bei Threadpool VerwendungCompletionService
für voneinander unabhängige TasksReihenfolgen, in der die Ausdrücke berechnet werden können:
;
\(3+4\) ;
\(3+7\);
\(1+2\) ;
\(3+7\)||
\(3+4\) ;
3+7pp.04.01-RunnableReturn
ExecutorService
für asynchrone Methodenaufrufe: Callable
und Future
Wie kann ein nebenläufiger Thread ein Ergebnis abliefern?
Reihenfolgen, in der die Ausdrücke berechnet werden können:
;
\(3+4\) ;
\(3+7\);
\(1+2\) ;
\(3+7\)||
\(3+4\) ;
3+7Callable
und Future
Callable
, Runnable
und Future
gemacht.Callable
und Runnable
repräsentieren die asynchron abzuarbeitende Aufgabe.Future
kann das Ergebnis einer asynchronen Berechnung abgerufen werden (get()
)FutureTask
ist eine Implementierung von Future
und Runnable
.ExecutorService
-FrameworkExecutorService
.submit
Callable
-Objekte an und starten call()
asynchron.Future
als Proxy, über den
Callable
asynchron mit ExecutorService
ausführenCallable
und Future
”pp.04.02-Future
Thread
-Instanziierung ist “teurer” (dauert länger) als bei anderen Klassen, denn Datenstrukturen zur Threadkontrolle müssen angelegt werden und threadlokaler Stack-Speicher angefordert werden.Thread
-Objekte werden frühzeitig (z.B. beim Start) vorbereitet (“Thread Pool”). Wird ein Thread benötigt, wird einer der vorbereiteten Threads aus dem Pool genommen, mit einem Runnable
-Objekt verbunden und (re-) aktiviert (statt start()
).run()
-Methode wird der Thread nicht vergessen und über die Garbage Collection entfernt, sondern deaktiviert und in den Thread Pool zur Wiederverwendung eingestellt.ExecutorService
-FrameworkDie Klasse Executors
stellt Factory-Methoden zur Erzeugung von Objekten zum ExecutorService
-Interface bereit:
newCachedThreadPool()
newFixedThreadPool
(nThreads: int)
newSingleThread
Executor()
shutdown()
ExecutorService
beginnt, herunterzufahren. Der Aufruf von shutdown()
ist aber asynchron (es geht direkt im Anschluss weiter im Programmablauf.isShutdown()
ExecutorService
bereits fertig terminiert ist (nach shutdown()
).shutdownNow()
ExecutorService
: Alle aktiven Tasks erhalten mit interrupt()
.ExecutorService
-Frameworkpool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4
pool-1-thread-5
pool-1-thread-3
pool-1-thread-4
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4
pool-1-thread-3
pool-1-thread-5
pool-1-thread-3
pool-1-thread-4
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-2
pool-1-thread-4
Executors
Factoryerzeugt bei Bedarf neue Threads, unbenutzte Threads werden nach 60 Sekunden beendet
\(\to\) Programme mit kurzlebigen, asynchronen Aufgaben
Executors
Factorygenau nThreads
werden erzeugt, überzählige Runnables
bzw. Callables
werden in Queue gespeichert
\(\to\) Programme mit sehr vielen unabhängigen Aufgaben
Executors
FactorySonderfall von newFixedThreadPool(1)
. Stürzt der Thread ab, wird er neu gestartet
\(\to\) Programme mit weniger unabhängigen Aufgaben; “Absturzsicherung”
Executors
FactoryAufgaben werden nach einer gegebenen Verzögerung bzw. periodisch ausgeführt
\(\to\) Programme mit vielen zeitlogisch abhängigen Aufgaben
Executors
FactorySonderfall von newScheduledThreadPool(1)
. Stürzt der Thread ab, wird er neu gestartet
\(\to\) Programme mit einigen zeitlogisch abhängigen Aufgaben; “Absturzsicherung”
ScheduledExecutorService
ScheduledExecutorService
bzw. dessen Implementierung ScheduledThread
PoolExecutor
erlaubt Aufgaben in Form von Callable
und Runnable
ExecutorService
wird durch die Factory Executor
erzeugt.ScheduledExecutorService
Dazu gibt es die Methoden:
schedule
scheduleAtFixedRate
scheduleWithFixedDelay
ScheduledExecutorService
var scheduler = Executors.newScheduledThreadPool(1);
var beeperHandle = scheduler.scheduleAtFixedRate(
() -> System.out.println("beep"), 3, 3, TimeUnit.SECONDS
);
scheduler.schedule(
() -> beeperHandle.cancel(true), 5 * 3, TimeUnit.SECONDS
);
scheduler.schedule(
() -> System.exit(0), (5 * 3) + 5, TimeUnit.SECONDS
);
pp.04.03-ThreadPoolSize
\[\begin{eqnarray} N_{CPU} &=& Anzahl~der~CPUs\\ &=& \mathtt{Runtime.getRuntime().availableProcessors()}\\ U_{CPU} &=& CPU~Auslastung~~~(0 < U_{CPU} \le 1)\\ \frac{W}{C} &=& Verh.~zwischen~Wartezeit~und~Rechenzeit\\ N_{Threads} &=& N_{CPU} \times U_{CPU} \times (1 + \frac{W}{C}) \end{eqnarray}\]
bei rechenintensiven Tasks (nie im BLOCKED
-, WAITING
- oder TIMED_WAITING
-Zustand -> \(\frac{W}{C}=0\)) auf einem halb ausgelasteten System (\(U_{CPU}=\frac12\)):
\[\begin{eqnarray} N_{Threads} &=& N_{CPU} \times \frac12 \times (1 + 0) = N_{CPU}\\ &=& \mathtt{Runtime.getRuntime().availableProcessors()/2} \end{eqnarray}\]
void execute(Runnable r)
r.run()
auftretende unbehandlete Exceptions werden sofort geworfen.Future<T> submit(Callable<T> c)
\(\to\) f
c.call()
auftretende unbehandelte Exceptions werden “aufgehoben” und erst von f.get()
geworfen.c.call()
behandelt werden (z.B. zum Aufräumen), kann sie danach im catch
-Block erneut geworfen werden (mit throw
), um sie auch dem Aufrufer von f.get()
weiterzuleiten.CompletionService
für voneinander unabhängige Tasks1var pool = Executors.newCachedThreadPool();
var tasks = new ArrayList<Callable<String>>();
tasks.add(() -> "calc c1");
tasks.add(() -> "calc c2");
tasks.add(() -> "calc c3");
var completionService = new ExecutorCompletionService<String>(pool);
for (var callableTask : tasks) {
completionService.submit(callableTask);
}
try {
for (var i = 0; i < tasks.size(); i++) {
var future = completionService.take();
System.out.printf("Result %2d: %s\n", i, future.get());
}
} catch (InterruptedException | ExecutionException e) {
// ...
}
pool.shutdown();