Synchronized keyword
synchronized (or synchronization)
When this Java keyword is used on a method or code block, it guarantees that at most one thread in the JVM executes that code at a time.
We force every thread to wait its turn before it can enter the method. That is, no two threads may enter the method or that code block at the same time.
JVM guarantees that Java synchronized code will only be executed by one thread at a time.
Concurrent access of shared objects in Java introduces two kind of errors:
- thread interference and
- memory consistency errors.
Synchronization in Java will only be needed if shared object is mutable.
If your shared object is read only or immutable object you don’t need synchronization despite running multiple threads. Same is true with what threads are doing with objects. If all the threads are only reading value then you don’t require synchronization in java.
- synchronized keyword in java provides locking which ensures mutual exclusive access of shared resource and prevent data race.
- synchronized keyword also prevents reordering of code statements by compiler which can cause subtle concurrent issues if we don’t use synchronized or volatile keyword.
- synchronized keyword involves locking and unlocking. Before entering into synchronized method or block, a thread needs to acquire the lock. At this point, it reads data from main memory than cache and when it releases the lock, it flushes write operation into main memory - which eliminates memory inconsistency errors.
You can have both static synchronized methods
and non static synchronized methods
and synchronized blocks
in java but we can not have synchronized variable in java. Using synchronized keyword with variable is illegal and will result in compilation error. Instead of java synchronized variable you can have java volatile variable, which will instruct JVM threads to read value of volatile variable from main memory and don’t cache it locally.
Method synchronization
Using synchronized keyword along with method is easy just apply synchronized
keyword to that method. What we need to take care is that static synchronized method locks on class object lock and non-static synchronized method locks on current object (this)
. And, it is possible that both static and non static java synchronized methods run in parallel. This is the common mistake a naive developer can do while writing java synchronized code.
public class Counter{
private static int count = 0;
public static synchronized int getCount(){
return count;
}
public synchoronized setCount(int count){
this.count = count;
}
}
In this example of java synchronization, the code is not properly synchronized because both getCount()
and setCount()
are not getting locked on the same object. If they run in parallel, that will result in getting incorrect count. Here getCount()
will lock in Counter.class
object while setCount()
will lock on current object (this). To make this code properly synchronized in java, we need to either make both method static or non static or use Block synchronization instead of Method synchronization.
Block synchronization
In Java, Block synchronization is preferred over method synchronization because by using block synchronization you only need to lock the critical section of code instead of whole method. Since java synchronization comes with cost of performance, we need to synchronize only part of code which absolutely needs to be synchronized.
Example of synchronized block in Java
Using synchronized block in java is also similar to using synchronized
keyword on methods. The important thing to note here is, if an object is used to lock synchronized block of code, Singleton.class
in below example is null, then java synchronized block will throw a NullPointerException.
public class Singleton{
private static volatile Singleton _instance;
public static Singleton getInstance() {
if(_instance == null) {
synchronized(Singleton.class) {
if(_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
}
This is a classic example of double checked locking in Singleton
. In this example, we make only critical section (part of code which is creating instance of singleton) synchronized and saved some performance. If we make the whole method synchronized, every call of this method will be blocked while you only need to create instance on the first call.
When using block synchronization, we have to be very careful about where the conditional checks are being evaluated. Look at the example below. This is an example where we want to set a class variable only once, and not do it again - for the lifetime of the container instance. The issue with the synchronized block in this example is, the if
condition to initialize the class variable carrierAccountProfile
is inside the synchronized block. So, the first time the if
condition is evaluated is the only time the class variable is set. After that, because of the conditional check if (carrierAccountProfile == null)
, the carrierAccountProfile
object will never be set again. If another request is made to this method with carrier-2
, the return object would still be the one related to carrier-`
. How would we fix this? There needs to be two class variables - one for carrier-1 and one for carrier-2. The conditional check needs to be outside of the synchronized block.
NOTE: STAY AWAY FROM CONVOLUTED CODE LIKE THIS. JUST USE CACHEING.
If you come across code like this in legacy applications, rewrite it to use Dependency Injection and caching
import java.text.MessageFormat;
class Scratch {
public void main(String[] args) throws Exception {
GenerateCarrierAccountProfile generateCarrierAccountProfile = new GenerateCarrierAccountProfile();
GenerateLabelRequest generateLabelRequest = new GenerateLabelRequest("carrier-1");
generateCarrierAccountProfile.getCarrierAccountProfile(generateLabelRequest);
}
class GenerateCarrierAccountProfile {
protected CarrierAccountProfile carrierAccountProfile;
protected CarrierAccountProfile getCarrierAccountProfile(GenerateLabelRequest request) throws Exception {
if (carrierAccountProfile == null) {
synchronized (this.getClass()) {
if (carrierAccountProfile == null) {
if (request.getCarrierName().equals("carrier-1")) {
// carrierAccountProfile = invoke a call to external service to retrieve details about carrier-1
carrierAccountProfile = new CarrierAccountProfile("carrier-1-name", "carrier-1-account-number", "carrier-1-details");
} else {
// carrierAccountProfile = invoke a call to external service to retrieve details about carrier-2
carrierAccountProfile = new CarrierAccountProfile("carrier-2-name", "carrier-2-account-number", "carrier-2-details");
}
if (carrierAccountProfile == null) {
throw new Exception("Carrier Account Info is Null.");
}
LOG.info(MessageFormat.format("CarrierAccountProfile initialized {0}", carrierAccountProfile));
}
}
}
if (carrierAccountProfile == null) {
throw new Exception("CarrierAccountProfile is null.");
}
return carrierAccountProfile;
}
}
class CarrierAccountProfile {
private String carrierName;
private String carrierAccountNumber;
private String otherCarrierDetails;
public CarrierAccountProfile(String carrierName, String carrierAccountNumber, String otherCarrierDetails) {
this.carrierName = carrierName;
this.carrierAccountNumber = carrierAccountNumber;
this.otherCarrierDetails = otherCarrierDetails;
}
}
class GenerateLabelRequest {
private String carrierName;
public GenerateLabelRequest(String carrierName) {
this.carrierName = carrierName;
}
public String getCarrierName() {
return carrierName;
}
}
}
Important points of synchronized keyword in Java
- Synchronized keyword in Java is used to provide mutual exclusive access of a shared resource with multiple threads in Java. Synchronization in java guarantees that no two threads can execute a synchronized method which requires same lock simultaneously or concurrently.
- You can use java synchronized keyword only on synchronized method or synchronized block.
- When ever a thread enters into java synchronized method or block it acquires a lock and whenever it leaves java synchronized method or block it releases the lock. Lock is released even if thread leaves synchronized method after completion or due to any Error or Exception.
- Java Thread acquires an object level lock when it enters into an instance synchronized java method and acquires a class level lock when it enters into static synchronized java method.
- java synchronized keyword is re-entrant in nature it means if a java synchronized method calls another synchronized method which requires same lock then current thread which is holding lock can enter into that method without acquiring lock.
- Java Synchronization will throw NullPointerException if object used in java synchronized block is null e.g. synchronized (myInstance) will throws NullPointerException if myInstance is null.
- One Major disadvantage of java synchronized keyword is that it doesn’t allow concurrent read which you can implement using java.util.concurrent.locks.ReentrantLock.
- One limitation of java synchronized keyword is that it can only be used to control access of shared object within the same JVM. If you have more than one JVM and need to synchronized access to a shared file system or database, the java synchronized keyword is not at all sufficient. You need to implement a kind of global lock for that.
- Java synchronized keyword incurs performance cost. Synchronized method in Java is very slow and can degrade performance. So use synchronization in java when it absolutely requires and consider using java synchronized block for synchronizing critical section only.
- Java synchronized block is better than java synchronized method in java because by using synchronized block you can only lock critical section of code and avoid locking whole method which can possibly degrade performance. A good example of java synchronization around this concept is getInstance() method Singleton class.
- Its possible that both static synchronized and non static synchronized method can run simultaneously or concurrently because they lock on different object.
- From java 5 after change in Java memory model reads and writes are atomic for all variables declared using volatile keyword (including long and double variables) and simple atomic variable access is more efficient instead of accessing these variables via synchronized java code. But it requires more care and attention from the programmer to avoid memory consistency errors.
- Java synchronized code could result in deadlock or starvation while accessing by multiple thread if synchronization is not implemented correctly. To know how to avoid deadlock in java see here.
- According to the Java language specification you can not use java synchronized keyword with constructor it’s illegal and result in compilation error. So you can not synchronized constructor in Java which seems logical because other threads cannot see the object being created until the thread creating it has finished it.
- You cannot apply java synchronized keyword with variables and can not use java volatile keyword with method.
- Java.util.concurrent.locks extends capability provided by java synchronized keyword for writing more sophisticated programs since they offer more capabilities e.g. Reentrancy and interruptible locks.
- java synchronized keyword also synchronizes memory. In fact java synchronized synchronizes the whole of thread memory with main memory.
- Important method related to synchronization in Java are wait(), notify() and notifyAll() which is defined in Object class.
- Do not synchronize on non final field on synchronized block in Java. because reference of non final field may change any time and then different thread might synchronizing on different objects i.e. no synchronization at all. example of synchronizing on non final field :
If you try to write synchronized code like the above example in java, you may get the warning “Synchronization on non-final field”.private String lock = new String("lock"); synchronized(lock){ System.out.println("locking on :" + lock); }
- Its not recommended to use String object as lock in java synchronized block because string is immutable object and literal string and interned string gets stored in String pool. so by any chance if any other part of code or any third party library used same String as there lock then they both will be locked on same object despite being completely unrelated which could result in unexpected behavior and bad performance. instead of String object its advised to use new Object() for Synchronization in Java on synchronized block.
private static final String LOCK = "lock"; //not recommended private static final Object OBJ_LOCK = new Object(); //better public void process() { synchronized(LOCK) { ........ } }
- From Java library Calendar and SimpleDateFormat classes are not thread-safe and requires external synchronization in Java to be used in multi-threaded environment.