Synchronization and Thread Safety Tutorial with Examples in Java

We need to think about various issues that fall under the broad description of thread safety. Generally, we need to take steps to make sure that different threads don't interact in negative ways:
  • if one thread is operating on some data or structure, we don't want another thread to simultaneously operate on that same data/structure and corrupt the results;
  • when Thread A writes to a variable that Thread B accesses, we need to make sure that Thread B will actually see the value written by Thread A;
  • we don't want one thread to hog, take or lock for too long a resource that other threads need in order to make progress.
Code that is safe to call by multiple threads simultanously is called thread safe. If a piece of code is thread safe, then it contains no race conditions. Race condition only occur when multiple threads update shared resources. Therefore it is important to know what resources Java threads share when executing.

Local Variables

Local variables are stored in each thread's own stack. That means that local variables are never shared between threads. That also means that all local primitive variables are thread safe.

Local Object References

Local references to objects are a bit different. The reference itself is not shared. The object referenced however, is not stored in each threads's local stack. All objects are stored in the shared heap. If an object created locally never escapes the method it was created in, it is thread safe. In fact you can also pass it on to other methods and objects as long as none of these methods or objects make the passed object available to other threads.

The only exception is of course, if one of the methods called with the LocalObject as parameter, stores the LocalObject instance in a way that allows access to it from other threads.

Object Members

Object members are stored on the heap along with the object. Therefore, if two threads call a method on the same object instance and this method updates object members, the method is not thread safe.

How to make a method thread-safe in Java?

There are basically below approaches to make an object thread safe in java :
  • Using Synchronized keyword
  • Make it immutable
  • Use a thread safe wrapper
  • Using Concurrency Locks
  • Using volatile keyword

Thread Safety using Synchronized keyword

The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements.

Synchronized Methods

To make a method synchronized, simply add the synchronized keyword to its declaration:
public class SynchronizedCounter {
    private int c = 0;
    public synchronized void increment() {
        c++;
    }
    public synchronized void decrement() {
        c--;
    }
    public synchronized int value() {
        return c;
    }
}

If count is an instance of SynchronizedCounter, then making these methods synchronized has two effects:
  • First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
  • Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.
Note that constructors cannot be synchronized — using the synchronized keyword with a constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the thread that creates an object should have access to it while it is being constructed.

Synchronized Statements

Another way to create synchronized code is with synchronized statements. Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:
public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}


In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other objects' methods. (Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.) Without synchronized statements, there would have to be a separate, unsynchronized method for the sole purpose of invoking nameList.add.

Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }
    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}


Use this idiom with extreme care. You must be absolutely sure that it really is safe to interleave access of the affected fields.

Thread Safety using Immutable Objects

An object is considered immutable if its state cannot change after it is constructed. Maximum reliance on immutable objects is widely accepted as a sound strategy for creating simple, reliable code.
Since the state of the immutable objects can not be changed once they are created they are automatically thread-safe.

How to create an immutable class?

To create an immutable class following steps should be followed:
  1. Create a final class.
  2. Set the values of properties using constructor only.
  3. Make the properties of the class final and private<\li>
  4. Do not provide any setters for these properties.
  5. If the instance fields include references to mutable objects, don't allow those objects to be changed:
    1. Don't provide methods that modify the mutable objects.
    2. Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
public final class FinalPersonClass { 
      private final String name; 
      private final int age; 
      public FinalPersonClass(final String name, final int age) { 
            super(); 
            this.name = name; 
            this.age = age; 
      } 
      public int getAge() { 
            return age; 
      } 
      public String getName() { 
            return name; 
      }       
}


Thread Safety Using a Thread Safe Wrapper

The synchronization wrappers add automatic synchronization (thread-safety) to an arbitrary collection. Each of the six core collection interfaces — Collection, Set, List, Map, SortedSet, and SortedMap — has one static factory method.
public static <t> Collection<t> synchronizedCollection(Collection<t> c);
public static <t> Set<t> synchronizedSet(Set<t> s);
public static <t> List<t> synchronizedList(List<t> list);
public static <k> Map<k> synchronizedMap(Map<k> m);
public static <t> SortedSet<t> synchronizedSortedSet(SortedSet<t> s);
public static <k> SortedMap<k> synchronizedSortedMap(SortedMap<k> m);


Each of these methods returns a synchronized (thread-safe) Collection backed up by the specified collection. To guarantee serial access, all access to the backing collection must be accomplished through the returned collection. The easy way to guarantee this is not to keep a reference to the backing collection. Create the synchronized collection with the following trick.

List<type>>list = Collections.synchronizedList(new ArrayList<type>());

A collection created in this fashion is every bit as thread-safe as a normally synchronized collection, such as a Vector.

In the face of concurrent access, it is imperative that the user manually synchronize on the returned collection when iterating over it. The reason is that iteration is accomplished via multiple calls into the collection, which must be composed into a single atomic operation. The following is the idiom to iterate over a wrapper-synchronized collection.
Collection<type> c = Collections.synchronizedCollection(myCollection);
synchronized(c) {
    for (Type e : c)
        foo(e);
}

Thread Safety using Concurrency Locks

A java.util.concurrent.locks.Lock is a thread synchronization mechanism just like synchronized blocks. A Lock is, however, more flexible and more sophisticated than a synchronized block.

Java Lock Example

Since Lock is an interface, you need to use one of its implementations to use a Lock in your applications. Here is a simple usage example:
Lock lock = new ReentrantLock();
lock.lock();
//critical section
lock.unlock();


First a Lock is created. Then it's lock() method is called. Now the Lock instance is locked. Any other thread calling lock() will be blocked until the thread that locked the lock calls unlock(). Finally unlock() is called, and the Lock is now unlocked so other threads can lock it.

Java Lock Implementations

The java.util.concurrent.locks package has the following implementations of the Lock interface:
  • ReentrantLock

Main Differences Between Locks and Synchronized Blocks

The main differences between a Lock and a synchronized block are:
  • A synchronized block makes no guarantees about the sequence in which threads waiting to entering it are granted access.
  • You cannot pass any parameters to the entry of a synchronized block. Thus, having a timeout trying to get access to a synchronized block is not possible.
  • The synchronized block must be fully contained within a single method. A Lock can have it's calls to lock() and unlock() in separate methods.

Thread Safety using volatile keyword

What is the Java volatile keyword?

Essentially, volatile is used to indicate that a variable's value will be modified by different threads.
Declaring a volatile Java variable means:
  • The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory";
  • Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.

Differences between synchronized and volatile are:

  • a primitive variable may be declared volatile (whereas you can't synchronize on a primitive with synchronized);
  • an access to a volatile variable never has the potential to block: we're only ever doing a simple read or write, so unlike a synchronized block we will never hold on to any lock;
  • because accessing a volatile variable never holds a lock, it is not suitable for cases where we want to read-update-write as an atomic operation (unless we're prepared to "miss an update");
  • a volatile variable that is an object reference may be null (because you're effectively synchronizing on the reference, not the actual object).
Attempting to synchronize on a null object will throw a NullPointerException.

Hope we are able to explain you java synchronization and thread safety, if you have any questions or suggestions please write to us using contact us form.(Second Menu from top left).

Please share us on social media if you like the tutorial.

Synchronization and Thread Safety Tutorial with Examples in Java
SHARE
    Blogger Comment
    Facebook Comment