[POTENTIAL BUG] Potential FIFO violation in BlockingQueue under high contention and suggestion for fair mode in ArrayBlockingQueue and LinkedBlockingQueue
Daniel Fuchs
daniel.fuchs at oracle.com
Wed Sep 4 11:11:32 UTC 2024
Hi Kim,
When several threads compete to put items in a queue,
regardless of queue locking strategies, nothing guarantees
the order in which items will end up in the queue if
the action of obtaining the next element and putting
it in the queue is not atomic. This is because a thread
can be preempted by the scheduler at any time between
two operations.
Consider the following scenario:
you have a sequence of ordered items a, b, c, one queue q,
and two threads T1, T2 and T3:
T1 starts, obtains element a, and gets preempted (the thread
is paused by the scheduler).
T2 starts, obtains element b and puts it in the queue.
T3 starts, obtains element c and gets preempted.
T1 gets scheduled again and puts a in the queue.
T3 gets scheduled again and puts c in the queue.
result: the queue contains b a c; regardless of whether
the queue uses fair or unfair locking, the order of items
in the queue will be unpredictable. And in this scenario,
there isn't even any contention on the queue.
To ensure that items will end up in the queue in the proper
order would require an additional lock at the application
level.
That is - you would need to use a lock so that taking
an element from the sequenceGenerator and putting it in the
queue becomes atomic (with respect to the queue).
so in your example you would need something like:
static ReentrantLock lock = new ReentrantLock();
...
Thread thread = new Thread(() -> {
lock.lock();
try {
var token = sequenceGenerator.getAndIncrement();
// even if this thread gets preempted here, no
// other thread will be able to obtain the next
// token before our token has been put in the queue
try {
queue.put(token);
} catch (InterruptedException e) {
// restore the sequence
var restored = sequenceGenerator.decrementAndGet();
assert token == restored;
}
} finally {
lock.unlock();
}
});
thread.start();
best regards,
-- daniel
On 04/09/2024 08:09, 김민주 wrote:
> // This configuration ensures items enter the queue's waiting list in a
> predictable sequence (10, 11, 12, ...) // allowing us to verify if they
> are later consumed in the same order for (int i = 0; i < PRODUCER_COUNT;
> i++) { Thread thread = new Thread(() -> { try {
> queue.put(sequenceGenerator.getAndIncrement()); } catch
> (InterruptedException e) { // ignore } });
More information about the core-libs-dev
mailing list