You should be aware of Synchronization if you are working on an application where multiple threads are accessing the same resources or else it will show you erroneous or unforeseen results.
Imagine a scenario where you are doing the multiple transactions in your bank. I believe you don’t want one thread is updating your balance and in parallel, the other thread is reading your balance otherwise you will end up in error or unexpected results.
Example 1:
Public class HDFCBank {
Protected long balanace =1000;
Public void deposit( int amount) {
this.balance = this.balance + amount;
}
}
Assume if two threads, A and B are executing the deposit method on the same instance of the HDFCBank Class then there will be no way to guess when the operating system is switching between these two threads.
Let’s assume current balance is 1000
1. A & B thread reads balance as 1000.
2. A will execute deposit(1000) and will add 1000 so total balance should be 2000
3. B will also execute deposit (500) but over here B’s balance is 1000 (read balance before step 2) so it will add 1000 +500 = 1500
You can observe that the total balance should be 2500 but we end up with only 1500 which is not we have expected. The above code in the deposit () contains a critical section and when multiple threads execute this critical section, race conditions occur. The situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions and the code section that leads to race conditions is called a critical section.
The above problem can be easily solved by ensuring that the deposit() will be used by only one thread at a time. The process by which this is achieved is called synchronization and Java provides different constructs for synchronization and locking e.g. volatile keyword, atomic variable, ReentrantLock, ReentrantReadWriteLock & Synchronized.
Today, I am only going to discuss about synchronization in details. The synchronization keyword in java creates a block of code referred to as the critical section. It is built around an internal entity known as the lock or monitor. Every object has a lock associated with it and by convention, a thread that needs consistent access to an object's fields has to acquire the object's lock before accessing them, and then release the lock when it's done with them and till then all other threads attempting to enter the locked monitor will be suspended until the first thread exits the monitor.
Let’s remodify the above example
Example 2:
public class HDFCBank {
Protected long balanace =1000;
public void synchronization deposit( int amount) {
this.balance = this.balance + amount;
}
}
Now, what will happen if threads A & B again executing the deposit() method?
· A & B thread will try to get the lock and let’s say A got the lock. In such case thread B will wait till the time A is not releasing the lock. A->balance = 1000;
· Will add 1000 to balance now total balance is 2000 and exit from deposit ()
· B thread will get the lock and NOW it will read balance value which will be 2000
· Add 500 to balance which will be 2500 as expected.
We can use synchronized keyword at the functional level or at block level but not with any variable and also it can be synchronized on the instance (object) or on the class object.
Synchronized Instance method
The above Example 2 is an example of instance method where you need to add synchronized keyword in the method declaration. This tells Java that the method is synchronized on the instance thus only one thread can execute at a time for one object. One thread one instance.
Assume HDFCBank has two instances hdfcBank1 & hdfcBank2 and each instance has its own two threads, T1 & T2 with hdfcBank1 and T3 and T4 with hdfcBank2.
Now, if we execute T1 and T2 at the same time then there will be no interference and T2 will wait for T1 to complete the task. Similarly, with T3 & T4 but if we execute all 4 threads then there can be interference between t1 and t3 or t2 and t4 because t1 acquires another lock and t3 acquires another lock.
Synchronized static method:
Similar to Example 2 but you have to add the static declaration at the method level.
Example 3:
public void static synchronization deposit( int amount) {
this.balance = this.balance + amount;
}
Over here, the deposit methods are synchronized on the class object of the class the synchronized static method belongs and we know only one class object exists in the Java VM per class therefore only one thread can execute inside a static synchronized method in the same class.
The problem that we saw in Synchronized Instance method can be easily solved by Synchronized static method.
Synchronized at block level:
There can be a scenario where you don’t want to synchronize the whole function but only the critical section or part of a method to be synchronized to an object or you want to lock an object for any shared resources. In all such situation, you can go with synchronized block.
Example 4:
public void deposit(int amount) {
System.out.println(“Depositing amount: ” + amount);
synchronized(this) {
this.balance = this.balance + amount;
}
System.out.println(“Total balance: ” + this.balance );
}
In Example 4, you must have observed that the synchronized block construct takes an object in parentheses (this) which is the instance the deposit method is called on. Over here, the execution is very similar to synchronized Instance methods i.e. only one thread can execute per instance.
Synchronized Blocks in Static Methods
Similar to Synchronized static method but over here you can pass the class object in the block.
Example 5:
public void static deposit(int amount) {
System.out.println(“Depositing amount: ” + amount);
synchronized(HDFCBank.class) {
this.balance = this.balance + amount;
}
System.out.println(“Total balace: ” + this.balance );
}
Points to remember while implementing synchronized keyword
· Use synchronized wherever necessary as it can slow and degrade performance and prefer block synchronization over method synchronization as it will only lock critical section of the code, not the whole method.
· Double check the object that you are using as monitor object in synchronized block as the uninitialized object will throw NULL POINTER EXCEPTION.
· Thread acquires an object level lock when it enters into an instance synchronized method and a class level lock when it enters into the static synchronized method and the lock is released even if thread leaves synchronized method after completion or due to any Error or Exception.
· Don’t use String object as a lock in java synchronized block as string is an immutable object and internally literal string gets stored in String pool. so, by any chance, if any other part of the code or any third-party library used same String as their lock then they both will be locked on the same object despite being completely unrelated which could result in unexpected behavior and bad performance.
Happy Coding…!!!