-
Bug
-
Resolution: Unresolved
-
P5
-
None
-
17.0.15
-
x86_64
-
linux
A DESCRIPTION OF THE PROBLEM :
We have discovered a potential deadlock issue in `java.util.concurrent.LinkedBlockingQueue` under high concurrency scenarios. Please find below a demo, problem description, and analysis for your review.
---
**1. Demo to Reproduce the Issue**
```java
import java.util.concurrent.LinkedBlockingQueue;
public class BQueueTest {
private final LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); // capacity 2
private volatile boolean flag = false;
public void producer() {
queue.offer(100);
flag = true;
}
public void consumer() {
try {
int data = queue.take();
} catch (InterruptedException ignored) {
}
}
public boolean test() throws InterruptedException {
Thread consumerThread = new Thread(this::consumer, "T2");
Thread producerThread = new Thread(this::producer, "T1");
consumerThread.start();
producerThread.start();
producerThread.join();
if (flag) {
consumerThread.interrupt();
producerThread.interrupt();
return true;
}
return false;
}
}
```
---
**2. Problem Description and Analysis**
In the implementation of `LinkedBlockingQueue`, the `take()` method will execute `notEmpty.await();` when the queue is empty, causing the thread to release the `takeLock` and enter a waiting state. However, if a CPU context switch occurs before the thread actually calls `notEmpty.await();`, and another thread calls `offer()` and tries to wake up the waiting thread via `signalNotEmpty()`, it cannot acquire the `takeLock` because it is still held by the previous thread. As a result, the `offer()` thread is added to the AQS queue and waits.
The previous thread, after calling `notEmpty.await();`, only temporarily releases the lock and does not actively wake up the waiting `offer()` thread, which leads to a deadlock where the `offer()` thread cannot proceed.
---
**3. Expected Result**
We hope the official team can pay attention to and fix this concurrency deadlock issue to avoid unexpected blocking in production environments.
---
**4. Additional Reproduction Information**
This issue can be easily reproduced by manually controlling thread switching using IntelliJ IDEA's multithreaded debugging features. However, due to the uncontrollable nature of thread scheduling, we have not been able to reproduce this deadlock reliably in a normal runtime environment.
---
Thank you for your attention and support!
REGRESSION : Last worked in version 17.0.15
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This issue can be easily reproduced by manually controlling thread switching using IntelliJ IDEA's multithreaded debugging features. However, due to the uncontrollable nature of thread scheduling, we have not been able to reproduce this deadlock reliably in a normal runtime environment.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no block on offer method
ACTUAL -
block on offer method
We have discovered a potential deadlock issue in `java.util.concurrent.LinkedBlockingQueue` under high concurrency scenarios. Please find below a demo, problem description, and analysis for your review.
---
**1. Demo to Reproduce the Issue**
```java
import java.util.concurrent.LinkedBlockingQueue;
public class BQueueTest {
private final LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); // capacity 2
private volatile boolean flag = false;
public void producer() {
queue.offer(100);
flag = true;
}
public void consumer() {
try {
int data = queue.take();
} catch (InterruptedException ignored) {
}
}
public boolean test() throws InterruptedException {
Thread consumerThread = new Thread(this::consumer, "T2");
Thread producerThread = new Thread(this::producer, "T1");
consumerThread.start();
producerThread.start();
producerThread.join();
if (flag) {
consumerThread.interrupt();
producerThread.interrupt();
return true;
}
return false;
}
}
```
---
**2. Problem Description and Analysis**
In the implementation of `LinkedBlockingQueue`, the `take()` method will execute `notEmpty.await();` when the queue is empty, causing the thread to release the `takeLock` and enter a waiting state. However, if a CPU context switch occurs before the thread actually calls `notEmpty.await();`, and another thread calls `offer()` and tries to wake up the waiting thread via `signalNotEmpty()`, it cannot acquire the `takeLock` because it is still held by the previous thread. As a result, the `offer()` thread is added to the AQS queue and waits.
The previous thread, after calling `notEmpty.await();`, only temporarily releases the lock and does not actively wake up the waiting `offer()` thread, which leads to a deadlock where the `offer()` thread cannot proceed.
---
**3. Expected Result**
We hope the official team can pay attention to and fix this concurrency deadlock issue to avoid unexpected blocking in production environments.
---
**4. Additional Reproduction Information**
This issue can be easily reproduced by manually controlling thread switching using IntelliJ IDEA's multithreaded debugging features. However, due to the uncontrollable nature of thread scheduling, we have not been able to reproduce this deadlock reliably in a normal runtime environment.
---
Thank you for your attention and support!
REGRESSION : Last worked in version 17.0.15
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This issue can be easily reproduced by manually controlling thread switching using IntelliJ IDEA's multithreaded debugging features. However, due to the uncontrollable nature of thread scheduling, we have not been able to reproduce this deadlock reliably in a normal runtime environment.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no block on offer method
ACTUAL -
block on offer method