Java - Aufteilung der Arbeit auf mehrere threads
Bin ich gestellt mit dem folgenden problem: ich brauche, um die Arbeit aufzuteilen auf mehrere threads für die perfomance-Gründen, aber ich bin nicht sicher, was Ansatz zu nehmen.
Erstens, die Aufgabe würde ich liefern sollte einen Wert zurückgeben und ein parameter. Außerdem die main-Methode (das tun die main-bisschen arbeiten, nicht static main()
) läuft bereits auf einem separaten thread und wird aufgerufen, in regelmäßigen Abständen. Zudem ist diese Methode muss irgendwann WARTEN Sie, bis alle threads fertig und dann gehen.
Einem Ansatz (die meisten offensichtlich für mich) ist, planen Sie jede Aufgabe auf einem separaten thread aus und speichern die Ergebnisse in der Klasse vars:
public Object result1, result2;
public void mainMethod() throws InterruptedException {
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
result1 = expensiveMethod("param1");
}
});
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
result2 = expensiveMethod("param2");
}
});
thread1.join();
thread.join();
//Do rest of work
}
private Object expensiveMethod(Object param){
//Do work and return result
}
Dies ist ein bisschen hässlich und nicht ideal, denn wie ich sagte, mainMethod ist aufgerufen, viele Male, und ich möchte nicht irgendwelche race-conditions auf die Einstellung der Ergebnis-Variablen. Im Idealfall würde ich mag, um Sie lokalen Variablen zu arbeiten, aber ich kann nicht machen Sie Sie zugänglich von innerhalb der run-Methode, es sei denn, Sie sind endgültig, und dann kann ich keine Werte zuweisen, um Sie...
Anderen Ansatz, den ich zwar zu tun hatte, war:
public void mainMethod() throws InterruptedException, ExecutionException {
String obj1, obj2;
final ExecutorService executorService = Executors.newFixedThreadPool(16);
final Future<String> res1 = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return expensiveMethod("param1");
}
});
final Future<String> res2 = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return expensiveMethod("param2");
}
});
obj1 = res1.get();
obj2 = res2.get();
}
private String expensiveMethod(String param) {
//Do work and return result
}
Diese automatisch wartet auf diese zwei Berechnungen, die von der main-Methode und erlaubt es mir zum speichern der Ergebnisse vor Ort. Was Sie Kerle denken? Andere Ansätze?
- Mit
Future
ist eine gute Idee.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Deinem Ansatz mit
ExecutorService
ist so ziemlich die modernste und sicherste Weg, dies zu tun. Es wird empfohlen, zu extrahieren IhreCallable
s zu trennen-Klasse:welche wird Ihr code viel sauberer:
Ein paar Hinweise:
16 threads sind zu viel, wenn Sie nur wollen, um die gleichzeitige Verarbeitung von zwei Aufgaben - oder vielleicht, die Sie wiederverwenden möchten, dass der pool von mehreren client-threads?
denken Sie daran in der Nähe der pool
verwenden Sie leichte
ExecutorCompletionService
warten, bis die erste Aufgabe fertig, nicht unbedingt für die erste abgegeben wurde.Wenn Sie brauchen eine völlig andere design-Idee, check out akka mit seiner Schauspieler-basierten concurrency-Modell.
Erstens, möchten Sie vielleicht zu externalisieren, die Schaffung von
ExecutorService
von IhremmainMethod()
Wenn dies immer Häufig aufgerufen, Sie möglicherweise erstellen eine Menge threads.Future
Ansatz ist besser als diese ist genau das, was Futures sind für. Auch, es macht das Lesen von code sehr viel einfacher.Auf einer leichteren note, obwohl Sie vielleicht die Definition der Objekte als final, Sie können immer setter-Methoden auf dem Objekt aufgerufen werden kann egal Ihre Referenz ist final oder nicht, potentiell so dass Sie zum ändern der Werte von final-Objekte. (Referenzen sind endgültig Objekte nicht!)
Wollen Sie das CompletionService und behalten Sie die übersicht der eingereichten Aufgaben.
In Ihrer Schleife Sie dann nehmen() und beenden die Schleife, wenn alle Aufgaben abgeschlossen.
Sehr gut skalieren wird, fügen Sie mehr Aufgaben später.
Ich soll hinzufügen, ein Vorschlag, der in meinen Augen eleganter aus als eine ganze neue Klasse für Ihre parametrisierten
Callable
. Meine Lösung ist eine Methode, die zurückgibt einenCallable
Beispiel:Dadurch wird sogar der client-code mehr schmackhaft:
Etwas anderen Ansatz ist:
erstellen Sie eine LinkedBlockingQueue
übergeben es an jeder Aufgabe. Aufgaben können Threads oder Runnables auf j.u.c.Executor.
jede Aufgabe addiert das Ergebnis mit dem queue
den Haupt-thread liest Ergebnisse mit Warteschlange.nehmen() in einer Schleife
Diese Weise die Ergebnisse behandelt werden, sobald Sie berechnet werden.