public void doSomething(){
Object[] a = new Object[5];
for(int i=0;i<5;i++) a[i] = executorService.submit(callable[i]).get();
}
Is this thread safe or not?
Thanks
public void doSomething(){
Object[] a = new Object[5];
for(int i=0;i<5;i++) a[i] = executorService.submit(callable[i]).get();
}
Is this thread safe or not?
Thanks
The code, as shown, does not contain any thread-unsafe behavior.
The evaluation of the expression callable[i]
occurs in the main thread, and the assignment of the result of get
to a[i]
also occurs on the main thread. You also get happens-before relationships helping with consistency: the call to submit
on the main thread happens-before any thread tasks, which happen-before the call to get
returns.
Of course, whatever actions callable[i]
itself performs must be done in a thread-safe manner, and furthermore this does not actually enable parallelism because you block the main thread waiting for each subtask before starting the next one.
Edit: You don't have an issue of conflicting writes in your edited code since every task accesses a different array index. The visibility issues outlined here shouldn't be an issue as (IIRC) the act of get()
ing the result implies a memory barrier which makes the writes visible.
TL;DR - Yes it is thread-safe.
The array in a
is thread confined:
a
is declared as a local variable, and no other thread can possibly see it1.a
to any other thread.Therefore the state of the array or the variable are not relevant to thread safety.
Likewise, i
is thread confined and not relevant.
There is a happens-before relationship between the call to submit and the thread that executes the task that ensures that callable[i]
' s state is fully visible to the callable's call
method when it runs.
The values that you are assigning into a
are the result of calling get()
on a Future
. The Future.get()
method is thread-safe. It ensures that there is a happens-before relationship between the thread that populated the Future
and the thread that calls get()
.
This all adds up to "thread safe".
1 - Another thread calling doSomething()
will have a different instance of the a
variable and it will contain a different Object[]
.
The only possible concerns are:
Is the callables
array safely published to this thread? (In the event that it was created by or populated by some other thread.) If not, then stale data may be used.
Are the Callable
instances in the callables
array thread-safe ... or effectively thread confined?
But these things are not really the "concern" of this code ... and cannot be addressed in this code.
There is one other thing to say about this example. This the following statement is actually completely synchronous.
a[i] = executorService.submit(callable[i]).get();
What it does is to submit a task to the executor and then wait (blocking the current thread!) until the task has finished. As a result, your 5 tasks will run one after another ... not in parallel
If you are trying to get some parallelism, then you should do something like this:
Object[] a = new Object[5];
Future<Object>[] f = new Future<>[5];
for (int i = 0; i < 5; i++) {
f[i] = executorService.submit(callable[i]);
}
for (int i = 0; i < 5; i++) {
a[i] = f[i].get();
}
If you are not trying to get parallelism, then this is better in almost all use-cases2:
Object[] a = new Object[5];
for (int i = 0; i < 5; i++) {
a[i] = callable[i].call();
}
2 - One case where it would be incorrect is if you were using a bounded executor service as a way of preventing resource utilization by a certain class of "intensive" tasks from swamping other things.