In Java, concurrency enables multiple threads to execute simultaneously, thereby enhancing performance and efficiency. However, improper handling of shared resources can cause serious issues in program behavior.
Concurrency-ProblemCommon Concurrency Problems
1. Race Condition
A race condition occurs when two or more threads access shared data simultaneously, and the outcome depends on the order in which the threads execute.
JavaclassCounter{intcount=0;voidincrement(){count++;// Not atomic: multiple threads may interfere}}publicclassGFG{publicstaticvoidmain(String[]args)throwsInterruptedException{Counterc=newCounter();// Thread 1 increments count 1000 timesThreadt1=newThread(()->{for(inti=0;i<1000;i++)c.increment();});// Thread 2 increments count 1000 timesThreadt2=newThread(()->{for(inti=0;i<1000;i++)c.increment();});// Start thread 1t1.start();// Start thread 2t2.start();// Wait for thread 1 to finisht1.join();// Wait for thread 2 to finisht2.join();// May be < 2000 due to race conditionSystem.out.println("Final Count: "+c.count);}}Explanation:
- Both threads increment the shared count variable concurrently.
- Since count++ is not atomic, intermediate updates can be lost, resulting in an incorrect final value.
Solution: Usesynchronized blocks or AtomicInteger to ensure thread-safe updates.
Java// Using AtomicIntegerimportjava.util.concurrent.atomic.AtomicInteger;classCounter{AtomicIntegercount=newAtomicInteger(0);voidincrement(){count.getAndIncrement();}}2. Deadlock
Adeadlock occurs when two or more threads are waiting for each other to release resources, causing all of them to be stuck forever.
JavaclassGFG{privatefinalObjectlock1=newObject();privatefinalObjectlock2=newObject();publicvoidmethodA(){// Acquire lock1synchronized(lock1){System.out.println("Thread 1: Holding lock 1");// Waits for lock2synchronized(lock2){System.out.println("Thread 1: Holding lock 2");}}}publicvoidmethodB(){// Acquire lock2synchronized(lock2){System.out.println("Thread 2: Holding lock 2");// Waits for lock1synchronized(lock1){System.out.println("Thread 2: Holding lock 1");}}}publicstaticvoidmain(String[]args){GFGexample=newGFG();newThread(example::methodA).start();newThread(example::methodB).start();}}OutputThread 1: Holding lock 1Thread 1: Holding lock 2Thread 2: Holding lock 2Thread 2: Holding lock 1
Explanation:
- Thread 1 acquires lock1 and waits for lock2.
- Thread 2 acquires lock2 and waits for lock1.
- Both threads are stuck, creating a deadlock.
Solution: Always get the locks in the same order in every thread. This way, threads won’t block each other.
Visibility Problem
This occurs when one thread modifies a variable, but other threads do not see the updated value due to caching or lack of synchronization.
JavaclassGFG{// Thread-unsafeprivatebooleanrunning=true;voidstart(){newThread(()->{while(running){}System.out.println("Stopped!");}).start();}// Change may not be visible to other threadvoidstop(){running=false;}publicstaticvoidmain(String[]args)throwsInterruptedException{GFGt=newGFG();t.start();// Short pause before stoppingThread.sleep(100);// Thread may not see this without volatilet.stop();}}Output: Time limit exceeded.
Explanation:
- Thread may cache the running variable, so changes made by stop() may not be immediately visible.
- This can cause the loop to never terminate.
Solution: Usevolatile keyword to ensure visibility:
private volatile boolean running = true;
This ensures that changes to running are immediately visible to all threads.
Starvation
Starvation occurs when a thread never gets CPU time or access to a resource because other high-priority threads keep executing.
JavaclassGFG{publicstaticvoidmain(String[]args){RunnablelowPriorityTask=()->{while(true){System.out.println("Low priority thread running...");}};RunnablehighPriorityTask=()->{while(true){System.out.println("High priority thread running...");}};Threadlow=newThread(lowPriorityTask);Threadhigh=newThread(highPriorityTask);low.setPriority(Thread.MIN_PRIORITY);high.setPriority(Thread.MAX_PRIORITY);low.start();high.start();}}Explanation:
- High-priority thread consumes CPU aggressively.
- Low-priority thread may get little or no execution time, leading to starvation.
Solution: Use fair locks or proper thread scheduling mechanisms to prevent starvation.
Explore
Java Basics
OOP & Interfaces
Collections
Exception Handling
Java Advanced
Practice Java