Wednesday, February 1, 2017

Multithreading Issues and Solutions in Java

Multithreading Issues and Solutions in Java: 
Race-Condition
Thread Safety
DeadLock
------------------------------------------------------------------------
Race-Condition: In Java it is a type of concurrency bug or issue which is introduced in your program because parallel execution 
of your program by multiple threads at same time, Since Java is a multi-threaded programming language hence risk of Race 
condition is higher in Java. This requires clear understanding of what causes a race condition and how to avoid that.
Race condition is of 2 types: "Check and Act" and "Read Modify Write"
The solution is use: Synchronization

Check and Act Race Condition:
If you call getInstance() method below from two thread simultaneously, then its possible that while one thread is initializing 
singleton after null check, another thread sees value of _instance reference variable as null (especially if your object takes longer
time to initialize) and enters into critical section which eventually results in getInstance() returning two separate instance of Singleton. 

    public Singleton getInstance(){
        if(_instance == null){   //race condition if two threads sees _instance= null
            _instance = new Singleton();
        }
    }
Solution: Use Synchronized in getInstance()

Read Modify Write Race Condition:
This comes due to improper synchronization of non-atomic operations or combination of two individual atomic operations which is not atomic 
when combined. Here both contains() and put() are atomic but still this code can result in race condition since both operation together is 
not atomic. Consider thread T1 checks for conditions and goes inside if block, now CPU is switched from T1 to thread T2 which also checks 
condition and goes inside if block. Now we have two thread inside if block which result in either T1 overwriting T2 value or vice-versa 
based on which thread has CPU for execution. In order to fix this race condition in Java you need to wrap this code inside synchronized 
block which makes them atomic together because no thread can go inside synchronized block if one thread is already there.

    if(!hashtable.contains(key)){  
        hashtable.put(key,value);
    }
    
Solution: Move these two codes lines inside Synchronized block.
------------------------------------------------------------------------

Thread Safety: Thread-safety or Thread-safe code in Java refers to code which can safely be used or shared in concurrent or 
multi-threading environment and they will behave as expected. Any code, Class or Object which can behave differently from its 
contract on concurrent environment is not thread-safe.

Ex: //Non Thread-Safe Class
    public class Counter {  
        private int count;  
        //This below method is not thread-safe because ++ is not an atomic operation
        public int getCount(){
            return count++;
        }  
    }

Above example is not thread-safe because ++ (increment operator) is not an atomic operation and can be broken down into 
Read, Update and Write operation. If multiple thread call getCount() approximately same time each of these three operation 
may coincide or overlap with each other for example while thread 1 is updating value, thread 2 reads and still gets old value, 
which eventually let thread 2 override thread 1 increment and one count is lost because multiple thread called it concurrently.

How to make above code thread safe in Java:
1) Use synchronized keyword in Java and lock the getCount() method so that only one thread can execute it at a time which 
   removes possibility of coinciding or interleaving.
2) use Atomic Integer, which makes this ++ operation atomic and since atomic operations are thread-safe and saves cost of external synchronization.

//Complete Code, Thread-Safe Example
public class Counter {
    private int count;
    //This method is thread-safe now because of locking and synchornization
    public synchronized int getCount(){
        return count++;
    }  
}

OR
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {    
    AtomicInteger atomicCount = new AtomicInteger(0);
    //This method is thread-safe because count is incremented atomically
    public int getCountAtomically(){
        return atomicCount.incrementAndGet();
    }
}

//Java Source code for method AtomicInteger.incrementAndGet()
public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))   //This does Compare and Set operation.
                return next;
        }
}

NOTE about Thread-Safety: 
1) Immutable objects are by default thread-safe because their states can't be modified once created. String is immutable, hence inherently thread-safe.
2) Read only or final variables in Java are also thread-safe.
3) Locking is one way of achieving thread-safety in Java.
4) Static variables if not synchronized properly becomes major cause of thread-safety issues.
5) Example of thread-safe class in Java: Vector, Hashtable, ConcurrentHashMap, String etc.
6) Atomic operations in Java are thread-safe. Ex: Reading a 32 bit integer from memory because its an atomic operation it can't interleave with other thread.
7) Local variables are also thread-safe because each thread has its own copy. Hence using local variables is good way for writing thread-safe code in Java.
8) In order to avoid thread-safety issue minimize sharing of objects between multiple thread.
9) Volatile keyword in Java can also be used to instruct threads not to cache variables and read from main memory.
10) Sometimes JVM plays a spoiler since it can reorder code for optimization, so the code which looks sequential and runs fine in development 
    environment are not guaranteed to run similarly in production environment because JVM may ergonomically adjust itself as server JVM and 
    perform more optimization and reorder which cause thread-safety issues. Volatile is the solution for variables if there are for such cases.
    Remember JVM tuning flag: server

------------------------------------------------------------------------
DeadLock: When two or more threads are waiting for each other to release lock and get stuck for infinite time, situation is called deadlock. 
It will only happen in case of multitasking. 
How to check if a code has possibility for Deadlock: Check if there are nested synchronized block or calling one synchronized method from other
or trying to get lock on different objects.

How to resolve DeadLock: When you are locked while running the application, take thread dump, in Linux you can do this by command "kill -3".
This will display status of all the threads and you can see which thread is locked on which object. Other way is to use jconsole, it will also 
show you exactly which threads are get locked and on which object.

Example of Deadlock:
package threads;
//Java program to create a deadlock by imposing circular wait.
public class DeadLockDemo {
    //This method request two locks, first String and then Integer
    public void method1() {
        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");

            synchronized (Integer.class) {  //nested Synchronized
                System.out.println("Aquired lock on Integer.class object");
            }
        }
    }

    //This method also requests same two locks but in exactly Opposite order i.e. first Integer and then String. 
    //This creates potential deadlock, if one thread holds String lock and other holds Integer lock and they wait 
    //for each other, forever.
    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}
/*
If method1() and method2() both will be called by two or many threads, there is a good chance of deadlock because if 
Thread-1 acquires lock on String object while executing method1() and Thread-2 acquires lock on Integer object while 
executing method2() both will be waiting for each other to release lock on Integer and String to proceed further which will never happen.
*/

Solution: Make the order of Locks in nested Synchronized same in both methods. So, if thread A acquires lock on Integer object, thread B 
will not proceed until thread A releases Integer lock, same way thread A will not be blocked even if thread B holds String lock because 
now thread B will not expect thread A to release Integer lock to proceed further.
------------------------------------------------------------------------

//References from java67 and javarevisited.

No comments:

Post a Comment