Java synchronized example
Introduction
Synchronization plays a key role in applications where multiple threads tend to share the same resources, especially if these resources must keep some kind of sensitive state where manipulations done by multiple threads at the same time could lead the resource to become in an inconsistent state.
In this tutorial we will see how we can use the synchronized keyword in order to make sure that a specific portion of a Java application may only be accessed by a single thread at a given time.
Intrinsic locks
Before diving into synchronization in Java we must first understand intrinsic locks.
Every Java object has an associated intrinsic lock (also commonly known as monitor). When a thread acquires an object's lock no other thread may acquire that same object's lock until the thread which acquired it in the first place releases the lock.
A thread acquires the intrinsic lock of a given object by invoking the synchronized statement on that same object:
public void someMethod() { synchronized (object) { // A thread that is executing this code section // has acquired object intrinsic lock. // Only a single thread may execute this // code section at a given time. } }
As soon as a thread leaves the synchronized section it will release the object's intrinsic lock. At this time any other thread may acquire the lock and execute the mutually exclusive synchronized code section.
If a thread reaches a synchronized code section while another thread is keeping the lock (and consequently executing that code section) it will wait for the other thread to finish and release the lock. Only then the thread will be able to acquire the lock and execute the synchronized code section.
Synchronized methods
We may also use the synchronized keyword at the method level:
public synchronized void someMethod() { // Do work }
As soon as a thread returns from the method the lock will be released, even if it returns from the method because of an uncaught exception.
In this case the intrinsic lock being acquired is the one that is associated with the current instance (referenced by this keyword). If every statement in someMethod is supposed to be synchronized then the previous method is equivalent to the following:
public void someMethod(){ synchronized(this){ // Do work } }
If we only need to synchronize a portion of a method we may use the following syntax:
public void someMethod(){ synchronized(this){ // Do synchronized work } // Do more work that is not synchronized other.doSomething(); }
It is considered best practice to invoke methods from other instances in sections that are not synchronized, especially if you are not the owner of the other objects implementation. Consider the following scenario:
public void someMethod(){ synchronized(this){ other.doSomething(); } } public void secondMethod(){ synchronized(this){ // Do work } }
Supposing that other's object doSomething method is also synchronized on the other object intrinsic lock, and that the same doSomething method in turn calls our object's secondMethod, in a scenario where multiple threads are executing methods on these two instances we could reach a deadlock situation.
Thread1 creates an instance of our object and calls its someMethod acquiring our intrinsic lock due to the synchronized statement. Meanwhile Thread2 happens to hold a reference to the same other instance and calls its doSomething method, acquiring the other intrinsic lock.
We know that doSomething method will call our secondMethod. We also know that our someMethod will call other's doSomething method. Since they are synchronized on distinct object's intrinsic locks they will stay waiting for each other for undetermined time. This is a deadlock.
Synchronizing static methods
Static methods may also be synchronized:
public static synchronized void staticMethod() { // Do synchronized work }
Since a current instance doesn't exist when we invoke static methods, the intrinsic lock will be the one associated with the class object.
Consider the following example:
public class SomeClass { public static synchronized void methodOne() { // Do work } public void methodTwo() { synchronized (SomeClass.class) { // Do work } } }
Synchronized methodOne and synchronized methodTwo's section will not be executed at the same time by distinct threads.
Practical example
Let's see a practical example:
public class Counter { private int counter; public synchronized void increment() { counter++; } public int read() { return counter; } }
In this scenario we have a synchronized counter. Distinct threads will not execute any synchronized method at the same time so we make sure that the counter will remain consistent. Writes to the counter will never be executed concurrently by distinct threads so it will be kept in a consistent state.
Remember that incrementing consists in reading the current value from memory, increment it and write it again to memory. If this operation was not synchronized it could become inconsistent (example: increments being lost).
If we have two distinct counters that are completely independent it doesn't make sense to keep increments to one counter to wait for the other counter, so we may create objects for locking purpose:
public class Counter { private int counterA; private int counterB; private final Object lockA = new Object(); private final Object lockB = new Object(); public void incrementA() { synchronized (lockA) { counterA++; } } public void incrementB() { synchronized (lockB) { counterB++; } } public int readA() { return counterA; } public int readB() { return counterB; } }
We are using Object instances to provide two distinct intrinsic locks. This way we may increment counterA and counterB independently, which improves throughput when compared to having a single lock that would unnecessarily synchronize access to both counters.
Wait and notify
When a thread holds an object's intrinsic lock it may invoke the wait operation on the object:
public void someMethod() throws InterruptedException { synchronized (lock) { lock.wait(); } }
In this case the thread will release the lock and wait until is is notified by any other thread that it may proceed. The wait operation may take an argument representing the time it should wait until proceeding.
The notify operation is like the following:
lock.notify();
The notify operation will wake a single waiting thread arbitrarily.
If we want to wake all waiting threads we use the notifyAlloperation:
lock.notifyAll();
Remember that even if multiple threads are awaken from waiting only one will proceed to execute inside the synchronized section. The others will also proceed when there is no other thread holding the lock (executing the synchronized section).
The same is true if we notify a single thread: It will only begin to execute when there are no other threads inside a synchronized section associated with the current lock.
Happens-before relationship
Finally we will also cover the happens-before relationship induced by the synchronized keyword. Consider the following methods of a given class:
public synchronized void increment() { counter++; } public synchronized int read() { return counter; }
By synchronizing both increment and read methods we are establishing a happens-before relationship between the two methods. If a given thread acquires the lock and increments the counter, any other thread that subsequently acquires the lock and reads the counter will read the value that was written by the previous thread.
If the methods were not synchronized and a given thread increments the counter, when a second thread reads the counter value it is not guaranteed that the second thread will read the value that was written by the first thread, even if the write happens before the read at wall clock time. This behaviour is due to the Java Memory Model specification.
The only way one may ensure that the second thread will always read the value written by the first thread is by the means of synchronization, or by declaring the counter volatile, but we will not cover the volatile modifier in this tutorial.
A lock implementation
If you wish to keep reading about Java synchronized statement and wait/notify idiom you may refer to the following article: Java explicit lock example.