Java Synchronization Introduction

In this tutorial we will discuss the Java synchronized keyword. This construct is used when multiple Java threads try to access the same shared mutable state concurrently. In a Multi-threaded environment this action can result in data visibility issues and data consistency issues. To prevent these issues we need to apply Java synchronization techniques when working with shared mutable state in Java.

One such practice is to use Java synchronized keyword to create Java synchronized blocks and Java synchronized methods. A common problem that occurs in a multi-threaded environment is a race condition when two or more threads attempt to update mutable shared data at the same time. We can use Java synchronization techniques to guard against these problems.

Why Java Synchronization

To demonstrate why java synchronization is needed lets consider a simple example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static class IncrementNumberTask implements Callable<Void> {
    private int current;

    public IncrementNumberTask() {
        this.current = 0;
    }

    @Override
    public Void call() throws Exception {
        doIncrement();
        return null;
    }

    public void doIncrement() {
        setCurrent(getCurrent() + 1);
    }

    public int getCurrent() {
        return current;
    }

    public void setCurrent(int current) {
        this.current = current;
    }
}

Above we create a simple task that increments a number when it is called. Now lets write a test to demonstrate a Java synchronization problem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Test
public void increment_notSynchronized() throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
    IncrementNumberTask incrementTask = new IncrementNumberTask();

    for(int i=0; i<1000; i++) {
        service.submit(incrementTask);
    }

    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, incrementTask.getCurrent());
}

Above we use the ExecutorService with three threads in the thread pool and we will increment an integer value to 1000 by submitting the increment task. After running this task we would expect the increment to have a value of 1000, but on running the above test it fails with an inconsistent result.

1
2
3
java.lang.AssertionError: 
Expected :1000
Actual   :990

The reason we don’t get the result that we expected is because the increment task does not employ synchronization techniques. The state of the increment task – current variable, is shared between the three threads, since each thread has the possibility of reading and writing to this state it is referred to as mutable state . When any of the threads in our executor service thread pool run the increment task they may see the data of this task in an inconsistent state. For example, they may not see the latest value of the tasks state or they might overwrite each others writes to that state.

One approach to resolving this issue is to use the Java synchronized keyword.

Java Synchronized Keyword

The Java synchronized keyword can be applied at different scopes. We can use it with:

  • static methods
  • instance methods
  • code blocks

The Java synchronized keyword associates the concept of a monitor which is associated with some object. When we use the synchronized keyword it creates a synchronized block that has a scope from the start to the end of the block. If we use the synchronized keyword with a method its scope is the method. Alternatively if the synchronized keyword is used with a code block, its scope is the block.

To prevent problems associated with concurrent access to shared mutable state, any access to that state should be protected by synchronization on the same object. All synchronized blocks are synchronized on the same object so that only one thread can be executing within that block at a time. Any other thread that attempts to enter that synchronized block are blocked until the thread that is executing within the synchronized block exits that block. Therefore, it is important that if you want to prevent concurrent access to some state of an object, you must use a synchronization block that is associated with the same object.

Synchronized Instance Methods

To create a synchronized method add the synchronized keyword to the method declaration to make the method synchronized. As we mentioned in the previous section, when you use the synchronized keyword it is associated with some object to realize the synchronization mechanism. In the case of the synchronized method it is associated with the object instance of the class to which the synchronized method belongs. Therefore, only one thread per instance of the class can execute this method.

We will now apply the synchronized keyword to fix the synchronization we saw with the IncrementNumberTask.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static class IncrementNumberSynchronizedTask implements Callable<Void> {

    private int current;

    public IncrementNumberSynchronizedTask() {
        this.current = 0;
    }

    @Override
    public Void call() throws Exception {
        doIncrement();
        return null;
    }

    public synchronized void doIncrement() {
        setCurrent(getCurrent() + 1);
    }

    public synchronized int getCurrent() {
        return current;
    }

    public synchronized void setCurrent(int current) {
        this.current = current;
    }
}

In the above code we create a new task IncrementNumberSynchronizedTask and we synchronized the doIncrement method:

1
2
3
public synchronized void doIncrement() {
    setCurrent(getCurrent() + 1);
}

As a result of synchronizing this method only one thread will be able to access the instance of this class at a time. Moreover, note that we also synchronized the getCurrent and setCurrent methods. This is because if one thread calls the setCurrent method without synchronization the synchronization used on the doIncrement method is pointless. Also, we use synchronizations with the getCurrent method to prevent visibility issues, as the CPU may cache the value of the current variable in a register and without synchronization the calling thread may not see the latest value of this variable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Test
public void increment_synchronized() throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
    IncrementNumberSynchronizedTask incrementTask = new IncrementNumberSynchronizedTask();

    for(int i=0; i<1000; i++) {
        service.submit(incrementTask);
    }

    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, incrementTask.getCurrent());
}

With the synchronization applied when we run the above test, the test passes with the current increment value equal to 1000.

Synchronized Static Methods

Static methods can be synchronized just like instance methods. These methods are synchronized on the Class object associated with the class, therefore since only one class object exists per loaded class inside the JVM only one thread can execute inside a static synchronized method, irrespective of the number of instances of the class.

Just like synchronized instance methods we add the synchronized keyword to the static method.

1
2
3
public static synchronized void doIncrement() {
    setCurrent(getCurrent() + 1);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Test
public void increment_synchronized() throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
    IncrementNumberSynchronizedTask incrementTask = new IncrementNumberSynchronizedTask();

    for (int i = 0; i < 1000; i++) {
        service.submit(incrementTask);
    }

    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, incrementTask.getCurrent());
}

Synchronized Blocks

Synchronized blocks have some advantages over synchronized methods in that they allow us to reduce the scope that a lock is held. This can offer some performance optimizations as we only need to synchronize the part of the code that accesses the shared mutable state.

1
2
3
4
5
public void doIncrement() {
    synchronized (this) { 
        setCurrent(getCurrent() + 1);
    }
}

In the above code the synchronized block takes on object as a parameter. In this case we passed the this pointer, this means that it will synchronize on the instance of class as with the synchronized method. This is a powerful mechanism because if we need to we can synchronize different parts of the objects state on different objects which could provide performance improvements. However, this practice is dangerous if not used correctly, for example, imagine we introduce a second method to our increment task that increments by 2.

1
2
3
4
5
public void doIncrementBy2() {
    synchronized (newObjectInstance) {
        setCurrent(getCurrent() + 2);
    }
}

Now we have two methods doIncrement which synchronizes on the object instance, and doIncrementBy2 which synchronizes on the object instance newObjectInstance. Therefore, as we synchronize on two different object instances it is possible that two different threads can perform the increment operation concurrently and produce an erroneous result. It is also important to be aware that if you synchronize on an object instance that instance should be immutable –unable to change.

1
private final Object newObjectInstance = new Object();

Alternatively to lock on an object across all instances of a class you can use a static reference.

1
private final static Object newObjectInstance = new Object();

We can now create a test to ensure we get the same result as with the synchronized method example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Test
public void increment_synchronized() throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
    IncrementNumberSynchronizedBlockTask incrementTask = new IncrementNumberSynchronizedBlockTask ();

    for (int i = 0; i < 1000; i++) {
        service.submit(incrementTask);
    }

    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, incrementTask.getCurrent());
}

We can also use synchronized blocks inside static methods.

1
2
3
4
5
public static void doIncrement() {
    synchronized (IncrementNumberSynchronizedStaticBlockTask.class) {
        setCurrent(getCurrent() + 1);
    }
}

Above we synchronize on the IncrementNumberSynchronizedStaticBlockTask class object so only one thread across all instances of this class can execute the code within the synchronized block.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Test
public void increment_synchronizedStatic() throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
    IncrementNumberSynchronizedStaticBlockTask incrementTask = new IncrementNumberSynchronizedStaticBlockTask();

    for (int i = 0; i < 1000; i++) {
        service.submit(incrementTask);
    }

    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
    assertEquals(1000, incrementTask.getCurrent());
}

Conclusion

In this tutorial we introduced the Java synchronization keyword that provides us with an approach to achieve thread synchronization in Java. We touched on some of the issues with concurrent access to shared mutable state such as: visibility issues, and race conditions.