-1
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

kajokiman
  • 5
  • 5

2 Answers2

4

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.

nanofarad
  • 40,330
  • 4
  • 86
  • 117
  • You may want to add that `get` is for sure thread safe: ["Memory consistency effects: Actions taken by the asynchronous computation happen-before actions following the corresponding Future.get() in another thread."](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/Future.html) – akuzminykh Jul 16 '20 at 01:14
4

TL;DR - Yes it is thread-safe.

The array in a is thread confined:

  • The variable a is declared as a local variable, and no other thread can possibly see it1.
  • You are not "publishing" the value in 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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • No it can't. `a` is a >>local variable<< declared in this method. It is not visible outside of this specific method call. There is no possibility that any other thread could see it. – Stephen C Jul 16 '20 at 02:55