Does Java Memory Model provide happens-before guarantee for Thread Pool interactions? In particular, will writes made by a thread pool worker thread before the end of running an item from a work queue be visible to a worker thread running the next item from the queue after that?
The specification (I personally find this FAQ useful: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization) states that "A call to start() on a thread happens before any actions in the started thread." or simply put, any memory writes you make before starting a thread will be executed before and visible to the run() method the started thread is going to execute. It is different for a thread pool, the start() would normally run before you make a write. Consider a simple workflow where a context object is mutated and passed to the next action:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
private static class Container<T> {
private T value;
public T get() {
return value;
}
public void set(T newValue) {
value = newValue;
}
}
public static void main(String[] args) {
final Container<Integer> sharedObject = new Container<>();
final ExecutorService executor = Executors.newFixedThreadPool(10);
// SKIPPED: pre-warm the executor so all worker threads are start()'ed
final Runnable read = () -> System.out.println("Got " + sharedObject.get());
Runnable write = () -> {
sharedObject.set(35);
executor.execute(read);
};
executor.execute(write);
// SKIPPED: wait until done
}
}
Is the write to sharedObject.value
by write.run()
guaranteed to be visible (not asking about ordering, this is obvious) to read.run()
?
(PS: I understand that making value
volatile
does provide this guarantee)
Update (complements the answer):
Package summary documentation for java.util.concurrent
summarizes memory consistency guarantees provided by the language and extended by the framework: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
This is the point where even though the visibility of the variables is guaranteed, the reordering of the instructions may lead to incorrect execution. And therefore, Java introduced the happens-before guarantee, with regards to the visibility of volatile variables. Happens-Before states about reordering.
Java memory model is divided between Thread Stacks (One for each thread) and a heap area. Thread Stack: It is a thread specific memory area and contains local variables, methods call information etc. JVM stacks could be of fixed size or variable size.
Java Thread pool represents a group of worker threads that are waiting for the job and reused many times. In the case of a thread pool, a group of fixed-size threads is created. A thread from the thread pool is pulled out and assigned a job by the service provider.
Once 'max' number of threads are reached, no more will be created, and new tasks will be queued until a thread is available to run them. In the general case, there is no 'right' way that thread pools work.
I think that it is guaranteed to be visible. ExecutorService
extends Executor
, and the javadocs for Executor
say:
Memory consistency effects: Actions in a thread prior to submitting a
Runnable
object to anExecutor
happen-before its execution begins, perhaps in another thread.
By my reading, that matches what is going on in your example. The write
runnable is submitting the read
runnable, so there is a happens-before relationship between events before the submission in the write
thread (i.e. the set
call) and the events afterwards in the read
thread (i.e. the get
call).
The fact that the write
runnable is itself submitted means that there is also a happens-before between the creation of the Container
object and the call to set
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With