为何ForkJoinPool.WorkQueue的push方法需用Unsafe.putOrderedObject而非直接赋值array[index]
Unsafe.putOrderedObject instead of direct array assignment in ForkJoinPool.WorkQueue.push()? Great question—this is a classic example of balancing concurrency correctness and performance in Java's core libraries. Let's walk through the reasoning behind using Unsafe.putOrderedObject here, using the provided code as context:
final void push(ForkJoinTask<?> task) { ForkJoinTask<?>[] a; ForkJoinPool p; int b = base, s = top, n; if ((a = array) != null) { // ignore if queue removed int m = a.length - 1; // fenced write for task visibility U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task); U.putOrderedInt(this, QTOP, s + 1); if ((n = s - b) <= 1) { if ((p = pool) != null) p.signalWork(p.workQueues, this); } else if (n >= m) growArray(); } }
Key Reasons for Using Unsafe.putOrderedObject
Guaranteeing Cross-Thread Memory Visibility
A plainarray[index] = taskassignment doesn't enforce any memory visibility rules across threads. When one thread adds a task to the queue, other worker threads might still see stale, cached versions of the array (thanks to CPU cache hierarchies) and miss the new task entirely.putOrderedObjectacts as a store-release operation: it ensures that the write to the array is flushed to main memory, making it visible to other threads eventually. This is non-negotiable forForkJoinPool, where workers need to reliably pick up new tasks as they're submitted.Balancing Performance vs. Strict Memory Barriers
You might wonder why not just use avolatilearray instead? Whilevolatiledoes provide visibility, it comes with stricter ordering constraints that add unnecessary overhead.putOrderedObjectskips enforcing ordering for subsequent memory operations relative to this write, but still guarantees visibility. ForForkJoinPool—which is optimized for high throughput in parallel tasks—this lighter-weight operation keeps things fast while maintaining correctness.Consistent Memory Semantics for Queue State
Notice the code also usesU.putOrderedIntto update thetopindex of the queue. UsingputOrderedfor both the task assignment and index update ensures that these two operations have matching memory visibility guarantees. If we mixed a plain array assignment withputOrderedInt, we could end up with a race condition where the updatedtopindex is visible to workers before the actual task is in the array—leading to attempts to access a null or uninitialized task.
Wrapping Up
In short, Unsafe.putOrderedObject is the sweet spot here: it gives us the cross-thread visibility we need for concurrent task submission, without the performance cost of stronger memory barriers that aren't required for this specific use case. This aligns perfectly with ForkJoinPool's design goal of efficient parallel task execution.
内容的提问来源于stack exchange,提问作者footmanff

