Java Thread Concurrency - ashwin-shetty/Documents-Wiki GitHub Wiki

When multiple threads share same objects variable there are possiblity of missing or overidding one of the previous update. This leads to data inconsistency.

In below example when two threads tries to increment counterI and counterJ. Actual Result should be 10 for both the counter( 5 update each from T1 and T2). As the operation is not thread safe. The output result will be 8,9 or 9,10 or 10,10

class ProcessingThread implements Runnable{
    private int countI;
    private int countJ;
    
    @Override
    public void run() {
        for(int i=1; i <= 5; i++){
            processSomethingI(i);
        	countI++;
        }

        for(int j=1; j <= 5; j++){
            processSomethingJ(j);
            countJ++;
        }
    }

    public int getCountI() {
        return this.countI;
    }
    public int getCountJ() {
        return this.countJ;
    }

    private  void  processSomethingI(int i) {
        // processing some job
        try {
            Thread.sleep(i*100); //Adding delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private   void  processSomethingJ(int j) {
        // processing some job
        try {
            Thread.sleep(j*100); //Adding delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}
       ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        //wait for threads to finish processing
        t1.join();
        t2.join();
        System.out.println("Processing count I ="+pt.getCountI());
        System.out.println("Processing count J ="+pt.getCountJ());

Methods to ensure Thread Safe

  1. synchronized Method , We can make both the methods synchronized so that only thread can access the block and the another thread waits until operation is complete. Issue with the synchronized method is only one synchronized method can be accessed at a given point of time by the threads in the list of synchronized method. Eg: When T1 is using processSomethingI() , T2 can't access processSomethingJ() even though both are independent blocks.
// Adding keyword synchronized to a method ensures method is part of synchronized block  
   private synchronized   void  processSomethingJ(int j) {
        // processing some job
        try {
            Thread.sleep(j*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  1. Using Lock, We can declare multiple Lock variable for each method so that all other methods can be used unlike synchronized block.
 Lock lockforI = new ReentrantLock();
private  void  processSomethingI(int i) {
        lockforI.lock(); //lock
        try {
            Thread.sleep(i*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lockforI.unlock(); //unlock
    }
  1. Using Atomic Variable, Using AtomicInteger instead of int. However this works on simple operation for complex we need to use locks .
class ProcessingThread implements Runnable{
    private AtomicInteger countI = new AtomicInteger();
    private AtomicInteger countJ = new AtomicInteger();

    @Override
    public void run() {
        for(int i=1; i <= 5; i++){
            processSomethingI(countI.getAndIncrement());
        }

        for(int j=1; j <= 5; j++){
           processSomethingJ(countJ.getAndIncrement());
        }
    }

    public int getCountI() {
        return this.countI.get();
    }
    public int getCountJ() {
        return this.countJ.get();
    }

    private  void  processSomethingI(int i) {

        try {
            Thread.sleep(i*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized   void  processSomethingJ(int j) {
        // processing some job
        try {
            Thread.sleep(j*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}
  1. ConcurrentHashMap can be used instead of HashMap to make maps thread safe.

  2. CopyOnWriteArrayList<>(); When only one few threads are used to update and many threads just reading then CopyOnWriteArrayList can be used. Here when the list is getting updated the old list will be used until the update is complete