Java - Thread interference, Race Condition and Synchronization

[Updated: Feb 7, 2017, Created: Mar 26, 2016]

In a multithreaded application, when multiple threads access and modify the 'same data', the behavior cannot always be predictable.

To understand the problem let's see an example:

public class ThreadInterferenceDemo {
    private Integer counter = 0;

    public static void main (String[] args) throws InterruptedException {
        ThreadInterferenceDemo demo = new ThreadInterferenceDemo();
        Task task1 = demo.new Task();
        Thread thread1 = new Thread(task1);

        Task task2 = demo.new Task();
        Thread thread2 = new Thread(task2);

        thread1.start();
        thread2.start();
    }

    private void performTask () {
        int temp = counter;
        counter++;
        System.out.println(Thread.currentThread()
                                 .getName() + " - before: "+temp+" after:" + counter);
    }

    private class Task implements Runnable {

        @Override
        public void run () {
            for (int i = 0; i < 5; i++) {
                performTask();
            }
        }
    }
}

Output:

On running above code multiple times, we occasionally see unexpected results. For example:

Thread-0 - before: 0 after:2
Thread-0 - before: 2 after:3
Thread-1 - before: 1 after:2
Thread-0 - before: 3 after:4
Thread-0 - before: 5 after:6
Thread-0 - before: 6 after:7
Thread-1 - before: 4 after:5
Thread-1 - before: 7 after:8
Thread-1 - before: 8 after:9
Thread-1 - before: 9 after:10

There are two problems: first line of output is incrementing the counter from 0 to 2. Second problem is, across multiple call of performTask() method, the counter increment should be gradual.

Both problems are caused by the fact that the two threads are occasionally interleaving each other. The interleaving threads are said to be interfering each other. The interfering threads' behavior is also termed as race condition.

Race conditions happen when the processes or threads depend on some shared state. Operations upon shared states are critical sections that must be mutually exclusive. Failure to obey this rule opens up the possibility of corrupting the shared state.

Using synchronized in methods

To avoid thread interference, Java provides a very easy solution for us, i.e. using the keyword synchronized in the method signature. Using this keyword requires each thread to get the 'Intrinsic Locks' before executing the method. This basically forces multiple threads to access the method one by one instead of executing it at the same time:

public class ThreadSynchronizedDemo {
    private Integer counter = 0;

    public static void main (String[] args) throws InterruptedException {
        ThreadSynchronizedDemo demo = new ThreadSynchronizedDemo();
        Task task1 = demo.new Task();
        Thread thread1 = new Thread(task1);

        Task task2 = demo.new Task();
        Thread thread2 = new Thread(task2);

        thread1.start();
        thread2.start();
    }

    private synchronized void performTask () {
        int temp = counter;
        counter++;
        System.out.println(Thread.currentThread()
                                 .getName() + " - before: "+temp+" after:" + counter);
    }

    private class Task implements Runnable {

        @Override
        public void run () {
            for (int i = 0; i < 5; i++) {
                performTask();
            }
        }
    }
}

Output:

Thread-0 - before: 0 after:1
Thread-0 - before: 1 after:2
Thread-0 - before: 2 after:3
Thread-1 - before: 3 after:4
Thread-1 - before: 4 after:5
Thread-1 - before: 5 after:6
Thread-1 - before: 6 after:7
Thread-1 - before: 7 after:8
Thread-0 - before: 8 after:9
Thread-0 - before: 9 after:10

In next tutorial we will look into the concept of 'Intrinsic Locks' in details.

Example Project

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.0.4

Java Thread Synchronization Example Select All Download
  • java-thread-synchronized
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also