Saturday, March 21, 2015

Synchronization block and atomic operations

When two or more threads modify some single object state we can get unexpected result. For example, we expect to get the value of object modified by Thread-A, but we are getting some other unexpected value because object was also modified by Thread-B.


class MyObject {
 private String val;

 public String getVal() {
  return val;
 }

 public void setVal(String val) {
  this.val = val;
  for (int iter = 0; iter < 1000; ++iter) {
   String str = "Useless Operation";
   str += iter;
  }
  System.out.println(this.val);  
 }
  
}


Thread-A changed the state of the object using the setVal, then Thread-B also changed the object state and Thread-A will output the value that has been set by Thread-B. That’s the code snippet of threads initialization


Thread thread_a = new Thread(new Runnable() {

 @Override
 public void run() {
  obj.setVal("1");

 }
});

Thread thread_b = new Thread(new Runnable() {

 @Override
 public void run() {
  obj.setVal("9");

 }
});
thread_a.start();
thread_b.start();

The output is going to be “9”, “9”. Surely, if you removed the useless cycle from setVal then you would get proper results because the value would be printed by Thread-A faster than the Thread-B changes it. Another option to prevent this behavior is using synchronized keyword.


Synchronized may be used with argument: synchronized(some_object), or without argument: in a method signature – it implicitly means that you are using synchronized with ‘this’ reference as an argument. Marking the method synchronized you tell all your threads that the operation with this method is atomic – method body operations are considered to be a single operation which can be executed by one and only one thread at the moment of time. 

Marking the method setVal ‘synchronized’ makes Thread-B wait until Thread-A completes the atomic operation (leaves synchronized block)
Atomic operations take effect because of locking mechanism in java, when the thread captures the object’s lock, getting into synchronized block no more threads may get to same block until the lock will be released. 
Every java object supports locking mechanism; it’s implemented by monitor object (which is a part of any java object). So when you hear/see words like lock or monitor you can think about it like about identical things. There is only one monitor per object and the object is specified as an argument of synchronized operator (don’t forget that that ‘synchronized’ in the method signature means synchronized(this)).

Static methods also can be synchronized, but in this case monitor belongs to java.lang.Class object which represents the whole class.

Let’s get deeper about monitors.
In order to implement locking mechanism monitors support the following methods (these methods you can find decompiling synchronized block of code):
monitorenter – lock capture
monitorexit – lock release
wait – thread moving to the monitor’s wait set (may be recognized as moving from RUNNING state to WAITING state) and awaiting for ‘notify’ function. 

When the thread (lock-holder) calls ‘wait’ method the lock is released and any other thread may capture the lock
notify(all) – one or all threads are awaken at the monitor’s wait set (actually, just removed from the wait set) and have some chances to capture the lock again.


How does the monitor capture the lock, and how does it release? How fast are these operations? The answer is: it depends on monitor locking type. There are three types of locking: thin, fat and biased.  More details about it in the nice answer from stackoverflow.com:

There are 3 type of locking in HotSpot
1.    Fat: JVM relies on OS mutexes to acquire lock.
2.    Thin: JVM is using CAS algorithm.
3.    Biased: CAS is rather expensive operation on some of the architecture. Biased locking - is special type of locking optimized for scenario when only one thread is working on object.
By default JVM uses thin locking. Later if JVM determines that there is no contention thin locking is converted to biased locking. Operation that changes type of the lock is rather expensive, hence JVM does not apply this optimization immediately. There is special JVM option -XX:BiasedLockingStartupDelay=delay which tells JVM when this kind of optimization should be applied.
Once biased, that thread can subsequently lock and unlock the object without resorting to expensive atomic instructions

Now we understand what atomic operation is and we may have some suggestions about atomic types like AtomicInteger, AtomicLong. Variables of these types are thread-safe by default, i.e. protected of concurrent modification by different threads at the same moment of time. What about variables marked with volatile? Operations on volatile variables are atomic, aren’t they?
Before answering we need to clarify what is happening when we declare variable as volatile. There are two levels of storing the variable value: memory and cache. For the sake of performance when the thread reads variable value from memory and modifies it the new value will not be immediately updated in the memory, it will be saved in the cache and eventually flushed back to the memory. For more than one thread we may have the following case: Thread-A reads and modifies value of variable A and saved it into a cache. Thread-B reads the value of variable A from the memory (Thread-B does not read the value from cache) and get the non-actual value which differs with value saved by Thread-A.

To prevent storing value in the cache the key word volatile is used, in case of volatile variable Thread-B will get an actual value of A, because its value was immediately saved in memory after modification by Thread-A. However marking variable as volatile doesn’t guarantee us that operations will be atomic. 
What do yo think about the code below is it thread-safe?


volatile Integer volatileVar = 1;
 
volatileVar++;

The code above can be considered as follows:


int temp = 0;
synchronized (volatileVar) {
 temp = volatileVar;
}

temp++;

synchronized (volatileVar) {
 volatileVar = temp;
}

It turns us out that if the value is read, modified and then assigned as a new one the result will not be thread-safe. What should we use now? Use classes from java.util.concurrent.atomic package. These classes are intended to provide its variables concurrent access and thread-safe modification operations (e.g. increment, decrement, add element to array). There are such classes like AtomicInteger, AtomicBoolean, AtomicLong, AtomicIntegerArray and so on can be found in java.util.concurrent.atomic. In fact these classes are wrappers for Integer, Boolean Long and other types with an improved concurrency support. For example, you can think about AtomicInteger like about a class which has volatile Integer field and every method where this field is used marked as synchronized. 

No comments:

Post a Comment