Tuesday 11 February 2014

Thread Safe

Thread-Safe is definitely one of the thing I want to write about. It simply something too important to ignore in a developer day of life. Not that it is a constant concern, it is also one of the most common source of error that we need to deal with.

WHAT IS THREAD SAFE
Come back to the earlier day, C developers rarely need to worry about thread. C does not support multithreading and the text book never mentioned about it as well. Things was changed when Java come to life. The language natively support multi-threading. The same portion of code in Java can be executed concurrently by more than 1 threads. Unfortunately, these threads can simultaneously read and write to the object state and interfere with each other. By definition, a piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads.

To illustrate the thread safe issue, we can take a look at this example.

public class Inverter { int value; public int invert(int origin){ value = origin; return value * -1; } }


Please do not laugh at the stupid implementation, this sample was created just to illustrate how multi-threading can spoil the functionality of your class. Assume that we have 2 threads that make use of the same inverter:

Thread 1: inverter.invert(10)
Thread 2: inverter.invert(20)

If you are extremely unlucky, thread 1 can execute up to assign origin to the field value but has not returned and thread 2 come to execute the same command. In this case, inverter.invert(10) will return -20 instead of -10. 

This issue is not rare. Actually, you will encounter it very often as lots of class in Java are not thread safe (for example SimpleDateFormatter, StringBuillder, ...)

HOW TO PREVENT THREAD SAFE ISSUE
Thread safe issue happen easily but also can be prevented easily. If we look deep into how things happen, thread safe issue can only occur when there are two conditions:

1/ Multiple thread access the same variable.
2/ The code require multiple atomic steps to complete. The code only function properly if there is no change to the variable at the middle of execution. 

Hence, to prevent thread safe issue, we should make sure this two conditions cannot happens together. However, thread safe prevention come at a price of performance reduction. That why, not all the class was created thread safe at the beginning. It is developer responsibility to prevent thread safe issue from happening.

A. No instance variable
Yep, we never need to worry about thread safe if there is nothing to share among threads. In Java, there is stack memory, heap memory and permgen memory. PermGen should not be our concern here because it is used to store class definition rather than variable. Generally, all Java objects and its instance variables are stored in heap space. However, return value, reference variable, local variables inside method are stored in stack memory. Stack is dedicated memory for each thread and therefore protected from first condition of thread safe.

Let say we fix the above class this way:

public class SafeInverter {
public int invert(int origin){
int value = origin;
return value * -1;
}
}

This class is equally stupid to earlier class but it is thread safe. The local variable "value" and return value of this method is stored in current thread stack memory. It will not accessible to any other thread. This method is highly recommended if you can achieve it. Sometimes, you do not have this luxury as instance variables are necessary for business logic. 

On the side note, it also worth highlighting in JVM do create a return variable for each non-void method. You may never know about its existence until you encounter a finally block that can overwrite your return value.

B. No sharing of object 
Assume that you keep the same Inverter class as original but you never share the inverter object, thread safe issue cannot happen as each thread access their own Inverter object.

Inverter inverter = new Inverter();
inverter.invert(10);

This method is pretty simple but its create burden for garbage collection as a lot of temporary object need to be created. Moreover, some constructors take long time to execute.  

C. Synchronize method and code
This solution aim to prevent the second condition of thread safe issue. It simply issue a lock of execution to method or block of code. Only one thread can execute the code at one time.

public class SynchronizedInverter {
private int value;
public synchronized int invert(int origin){
value = origin;
return value * -1;
}
}

This method simply disable the multi-threading support of Java and generally reduce performance. However, if the portion of code that need to be synchronize is short enough and not too many threads are being run, you can use this method.
There is one more variation of this method where we create threadsafe wrapper for the object.

public class InverterWrapper {
Inverter inverter = new Inverter();
public synchronized int invert(int origin) {
return inverter.invert(origin);
}
}

Use this solution when you do not have access to the original method or when you want to provide both threadsafe and non threadsafe version to user. Java collection framework use this approach. 

D. Object Pool
Object Pool is the combination of both solution B and C. You use Object pool when you want to avoid the pain of keep creating new object:

import java.util.Stack;
public class InverterPool {
static Stack<Inverter> inverters = new Stack<Inverter>();
public static synchronized Inverter getInverter(){
if (inverters.isEmpty()){
return new Inverter();
}
else {
return inverters.pop();
}
}

public static synchronized void pushBackInverter(Inverter inverter){
inverters.push(inverter);
}
}

Inverter inverter = InverterPool.getInverter();
inverter.invert(10);
InverterPool.pushBackInverter(inverter);

With this implementation, you still cannot avoid using synchronize method but you limit it to short and simple method. You also cannot avoid creating some Inverter but you can reuse many objects and minimize creating too many object. So, this method is recommended when the object creation take a lot of resource or the code that vulnerable to thread safe issue is long.




2 comments: