JVM.Threads.What are the common thread-related issues in JVM, and how can they be debugged?

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?

  1. Run jstack <PID> and look for “Deadlock detected”.
  2. Use JConsole or VisualVM → Monitor threads under “Thread” tab.
  3. 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

IssueCauseFixDebugging
Race ConditionsShared variable modified by multiple threadsUse synchronized, AtomicIntegerjstack, logs
DeadlocksThreads waiting for each other’s locksLock ordering, use ReentrantLockjstack, ThreadMXBean
StarvationLow-priority threads never get CPU timeFair locks (ReentrantLock(true))jconsole, logs
Memory VisibilityChanges not seen by other threadsUse volatile, synchronizedLogs, thread monitoring
Thread Pool OverloadToo many tasks in the poolBounded queues, monitor active threadsVisualVM, thread metrics

🚀 Final Thoughts

  • Use tools like jstack, VisualVM, and ThreadMXBean 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.
This entry was posted in Без рубрики. Bookmark the permalink.