问题
There is Java code which when simplified looks like this:
while(someCondition)
{
SomeType a = CalcResult(param1);
SomeType b = CalcResult(param2);
SomeType c = CalcResult(param3);
// Do something with a, b and c
}
CalcResult()
is time consuming. The application runs on an SMP system. There is a push to try running all three calculations simultaneously each on its own CPU instead of running them sequentially. It is always those 3 tasks that need to be parallel, not an arbitrary number of tasks (such is the algorithm). Each task may take slightly more or less time than others, but generally the variances are not so large (20-30%).
As they need to return a result, I looked at the Executor service solutions such as this from https://stackoverflow.com/a/9148992/2721750:
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() {
return 2;
}
};
Future<Integer> future = executor.submit(callable);
// future.get() returns 2
executor.shutdown();
As my experience with Java is mainly in servlet/JSP development I have no experience with threads and not sure if that snippet would work with 3 tasks instead of one.
How can I submit 3 tasks, each with its own parameter value, and wait untill all of them return the result of calculation, simultaneously making sure that creating threads for them does not negate the advantage of running on their own CPUs, i.e. is there a way to create the threads once before the while()
loop strarts, then simply shove a new paramN
into each of the threads inside the loop, waking them up, and wait until they performed all of the calculations?
回答1:
Executors.newSingleThreadExecutor()
will create only a single thread. What you want is Executors.newFixedThreadPool(3)
. Call this before the while loop, so the threads are only created once.
Create a Callable wrapper:
class MyCallable implements Callable<V> {
P p;
MyCallable(P parameter) {
p = parameter;
}
V call() {
return CalcResult(p);
}
}
while loop:
ExecutorService executor = Executors.newFixedThreadPool(3);
while (cond) {
Future<V> aFuture = executor.submit(new MyCallable(param1));
Future<V> bFuture = executor.submit(new MyCallable(param2));
Future<V> cFuture = executor.submit(new MyCallable(param3));
// this will block until all calculations are finished:
a = aFuture.get();
b = bFuture.get();
c = cFuture.get();
// do something with a/b/c, e.g. calc new params.
}
回答2:
I have no experience with Java threads
Then you are playing with fire here. Concurrency is tricky because there are failure modes that are not possible with sequential programming, and yet the runtime does not save you from your own mistakes. So before you go pasting code from SO, you should probably go read at least chapters 1-6 of Java Concurrency in Practice to understand what's going on.
回答3:
You could create an executor service for your application, specifically for processing these calculations. The pool size may vary depending on if you ever were going to run multiple calculations at the same time:
ExecutorService service = Executors.newFixedThreadPool(3);
Future<Integer> submitA = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return processA();
}
});
Future<Integer> submitB = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return processB();
}
});
Future<Integer> submitC = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return processC();
}
});
int result = submitA.get() + submitB.get() + submitC.get();
In this case you would want to make sure that you are not creating the Thread pool each time you run the calculation, in that case the impact of creating the pool will be minor compared to the saving - assuming that the running time will be reduced by splitting the task.
来源:https://stackoverflow.com/questions/28632205/using-executorservice-to-repeatedly-perform-a-number-of-similar-tasks-in-paralle