1. Common Thread Issues & How to Fix Them
1️⃣ Race Conditions
🔹 What Happens?
- Multiple threads access shared data without synchronization, causing inconsistent results.
- Example:
private int counter = 0;
public void increment() {
counter++; // ❌ Not thread-safe!
}
- Expected:
counter = 1000
Actual: Might be lower due to lost updates.
🔹 How to Fix?
✅ Use synchronization (synchronized
, ReentrantLock
, or AtomicInteger
).
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet(); // ✅ Thread-safe
}
🔹 How to Debug?
- Use thread dumps (
jstack
) to check which threads are modifying shared variables. - Use race condition detection tools like FindBugs or Intel Inspector.
2️⃣ Deadlocks
🔹 What Happens?
- Two or more threads wait on each other’s locks indefinitely.
🔹 Example:
class DeadlockExample {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void methodA() {
synchronized (lockA) {
synchronized (lockB) { // ❌ Circular dependency
System.out.println("Method A");
}
}
}
public void methodB() {
synchronized (lockB) {
synchronized (lockA) { // ❌ Deadlock risk
System.out.println("Method B");
}
}
}
}
🔹 How to Fix?
✅ Always acquire locks in the same order.
public void safeMethod() {
synchronized (lockA) {
synchronized (lockB) { // ✅ Prevents deadlock
System.out.println("Safe Method");
}
}
}
🔹 How to Debug?
- Run
jstack <PID>
and look for “Deadlock detected”. - Use JConsole or VisualVM → Monitor threads under “Thread” tab.
- Use
ThreadMXBean
to detect deadlocks programmatically:
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = bean.findDeadlockedThreads();
if (deadlockedThreads != null) {
System.out.println("Deadlock detected!");
}
3️⃣ Thread Starvation
🔹 What Happens?
- High-priority threads keep executing, preventing low-priority threads from running.
- Example:
Thread highPriority = new Thread(() -> {
while (true) System.out.println("High-priority running");
});
highPriority.setPriority(Thread.MAX_PRIORITY); // 🚨 May starve other threads
highPriority.start();
🔹 How to Fix?
✅ Use fair locks (ReentrantLock(true)
).
private final ReentrantLock lock = new ReentrantLock(true); // ✅ Enables fairness
🔹 How to Debug?
- Use
jconsole
to check thread priorities. - If some threads never run, adjust thread priorities dynamically.
4️⃣ Memory Visibility Issues
🔹 What Happens?
- A thread modifies a variable, but other threads don’t see the update due to CPU caching.
🔹 Example (Without volatile
):
private boolean running = true;
public void run() {
while (running) { // ❌ May never see the update
}
}
public void stop() {
running = false;
}
🔹 How to Fix?
✅ Use volatile
to ensure visibility across threads.
private volatile boolean running = true;
🔹 How to Debug?
- Add logging (
System.out.println()
). - Use ThreadMXBean to analyze memory access.
5️⃣ Thread Pool Overload
🔹 What Happens?
- Too many tasks overwhelm a fixed thread pool, leading to OutOfMemoryError or slow execution.
🔹 Example:
ExecutorService executor = Executors.newFixedThreadPool(2); // ❌ Only 2 threads
for (int i = 0; i < 1000; i++) {
executor.submit(() -> { /* Heavy task */ });
}
🔹 How to Fix?
✅ Use a bounded queue and monitor active threads.
ExecutorService executor = new ThreadPoolExecutor(
2, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(50)); // ✅ Controls backlog
How to Debug?
- Use VisualVM → Inspect thread pool usage.
- Use
ThreadPoolExecutor.getActiveCount()
to log active threads.
2. Debugging Thread Issues in Java
1️⃣ Using jstack
(Thread Dump)
- Run
jstack <PID>
to list all threads and their states. - Helps identify deadlocks, blocked threads, and high CPU usage.
2️⃣ Using VisualVM
- Attach to a running JVM and inspect thread states, CPU usage, memory allocation.
3️⃣ Using jconsole
- Monitor live threads and detect deadlocks.
4️⃣ Using ThreadMXBean
- Programmatically analyze CPU time, thread count, deadlocks
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
System.out.println("Thread Count: " + bean.getThreadCount());
3. Summary Table
Issue | Cause | Fix | Debugging |
---|---|---|---|
Race Conditions | Shared variable modified by multiple threads | Use synchronized , AtomicInteger | jstack , logs |
Deadlocks | Threads waiting for each other’s locks | Lock ordering, use ReentrantLock | jstack , ThreadMXBean |
Starvation | Low-priority threads never get CPU time | Fair locks (ReentrantLock(true) ) | jconsole , logs |
Memory Visibility | Changes not seen by other threads | Use volatile , synchronized | Logs, thread monitoring |
Thread Pool Overload | Too many tasks in the pool | Bounded queues, monitor active threads | VisualVM, thread metrics |
🚀 Final Thoughts
- Use tools like
jstack
,VisualVM
, andThreadMXBean
for live thread analysis. - Choose the right concurrency mechanisms (e.g.,
AtomicInteger
for counters,ReentrantLock
for fairness). - Profile and test your application under load to detect thread issues early.