In this post, I will throw light upon Java synchronized keyword. How to use it, what are the differences in implementation and best practices.
First let’s understand where synchronized keyword is used. We use it when we want to limit access to block of code to only one thread. In the example below, we have 3 threads. First and second thread simultaneously try to modify value1 of MyClass, last thread(TimerTask) continuously(every 1 second) prints out state of instance.
SynchronizedExample1.class
import java.time.Instant; import java.util.Timer; import java.util.TimerTask; import static java.util.concurrent.TimeUnit.SECONDS; public class SynchronizedExample1 { public static void main(String[] args) { MyClass myClass = new MyClass(); new Thread(() -> { myClass.setValue1(5); }).start(); new Thread(() -> { myClass.setValue1(7); }).start(); new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Instant.now() + " : " + myClass); } }, 0, 1000); } private static class MyClass { private Integer value1; public Integer getValue1() { return value1; } public synchronized void setValue1(Integer value1) { this.value1 = value1; ThreadSleepInSeconds(5); } private void ThreadSleepInSeconds(int seconds) { try { SECONDS.sleep(seconds); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "MyClass{" + "value1=" + value1 + '}'; } } }
A result of execution:
2018-11-26T05:07:06.704Z : MyClass{value1=5} 2018-11-26T05:07:07.706Z : MyClass{value1=5} 2018-11-26T05:07:08.711Z : MyClass{value1=5} 2018-11-26T05:07:09.715Z : MyClass{value1=5} 2018-11-26T05:07:10.719Z : MyClass{value1=5} 2018-11-26T05:07:11.722Z : MyClass{value1=7} 2018-11-26T05:07:12.724Z : MyClass{value1=7} 2018-11-26T05:07:13.725Z : MyClass{value1=7} 2018-11-26T05:07:14.727Z : MyClass{value1=7}
A thread which first gets access to setValue1() method keeps a “lock” on the instance for 5 seconds(line in above code 39), so no other thread can modify or read any data in the whole object. Thread 2 modifies value1 only when Thread 1 exited. We made setValue1() method intentionally sleep for 5 seconds to prove that no other thread can access object, until “lock” is released, e.g. thread 1 finish it’s job.
Let’s look at another example:
SynchronizedExample2.class
import java.time.Instant; import java.util.Timer; import java.util.TimerTask; import static java.util.concurrent.TimeUnit.SECONDS; public class SynchronizedExample2 { public static void main(String[] args) { MyClass myClass = new MyClass(); new Thread(() -> { myClass.setValue1(5); }).start(); new Thread(() -> { myClass.setValue2(7);//new code }).start(); new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Instant.now() + " : " + myClass); } }, 0, 1000); } private static class MyClass { private Integer value1; private Integer value2;//new code public Integer getValue1() { return value1; } public synchronized void setValue1(Integer value1) { this.value1 = value1; ThreadSleepInSeconds(5); } public Integer getValue2() {//new code return value2; } public synchronized void setValue2(Integer value2) {//new code this.value2 = value2; } private void ThreadSleepInSeconds(int seconds) { try { SECONDS.sleep(seconds); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "MyClass{" + "value1=" + value1 + ", value2=" + value2 + '}'; } } }
A result of execution:
2018-11-26T05:28:29.814Z : MyClass{value1=5, value2=null} 2018-11-26T05:28:30.814Z : MyClass{value1=5, value2=null} 2018-11-26T05:28:31.819Z : MyClass{value1=5, value2=null} 2018-11-26T05:28:32.821Z : MyClass{value1=5, value2=null} 2018-11-26T05:28:33.824Z : MyClass{value1=5, value2=null} 2018-11-26T05:28:34.828Z : MyClass{value1=5, value2=7} 2018-11-26T05:28:35.832Z : MyClass{value1=5, value2=7}
In the example above even though we are trying to modify value2 in thread 2, we still can’t do it, because thread 1 accessed synchronized method first and keeps “lock”. So we have to wait until it will finish before we can access another synchronized method.
Examples above used synchronized keyword in the method declaration. We can use synchronized keyword a little bit different, but it will work exactly the same as above code.
SynchronizedExample3.class
import java.time.Instant; import java.util.Timer; import java.util.TimerTask; import static java.util.concurrent.TimeUnit.SECONDS; public class SynchronizedExample3 { public static void main(String[] args) { MyClass myClass = new MyClass(); new Thread(() -> { myClass.setValue1(5); }).start(); new Thread(() -> { myClass.setValue2(7); }).start(); new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Instant.now() + " : " + myClass); } }, 0, 1000); } private static class MyClass { private Integer value1; private Integer value2; public Integer getValue1() { return value1; } public void setValue1(Integer value1) { synchronized (this) {//new code this.value1 = value1; ThreadSleepInSeconds(5); } } public Integer getValue2() { return value2; } public void setValue2(Integer value2) { synchronized (this) {//new code this.value2 = value2; } } private void ThreadSleepInSeconds(int seconds) { try { SECONDS.sleep(seconds); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "MyClass{" + "value1=" + value1 + ", value2=" + value2 + '}'; } } }
A result of execution:
2018-11-26T05:36:31.808Z : MyClass{value1=5, value2=null} 2018-11-26T05:36:32.812Z : MyClass{value1=5, value2=null} 2018-11-26T05:36:33.816Z : MyClass{value1=5, value2=null} 2018-11-26T05:36:34.820Z : MyClass{value1=5, value2=null} 2018-11-26T05:36:35.823Z : MyClass{value1=5, value2=null} 2018-11-26T05:36:36.824Z : MyClass{value1=5, value2=7} 2018-11-26T05:36:37.828Z : MyClass{value1=5, value2=7} 2018-11-26T05:36:38.832Z : MyClass{value1=5, value2=7}
Look at methods setValue1() and setValue2(), we have implemented synchronized in different way, but it still works the same. Those 2 implementations exactly the same and there is no difference in the functionality of those approaches. This could be sometimes misleading because you will expect to synchronize the only small block of code, but you still actually locking the whole object.
public synchronized void setValue2(Integer value2) { this.value2 = value2; } //equal to public void setValue2(Integer value2) { synchronized (this) { this.value2 = value2; } }
To fix an issue of accessing different synchronized methods by different threads you should use separate locks because when you use this keyword, you are using single lock(locking on the object). We will fix it in below example.
import java.time.Instant; import java.util.Timer; import java.util.TimerTask; import static java.util.concurrent.TimeUnit.SECONDS; public class SynchronizedExample4 { public static void main(String[] args) { MyClass myClass = new MyClass(); new Thread(() -> { myClass.setValue1(5); }).start(); new Thread(() -> { myClass.setValue2(7); }).start(); new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Instant.now() + " : " + myClass); } }, 0, 1000); } private static class MyClass { private Integer value1; private Integer value2; private final Object lock1 = new Object();//new code private final Object lock2 = new Object();//new code public Integer getValue1() { return value1; } public void setValue1(Integer value1) { synchronized (lock1) {//new code this.value1 = value1; ThreadSleepInSeconds(5); } } public Integer getValue2() { return value2; } public void setValue2(Integer value2) { synchronized (lock2) {//new code this.value2 = value2; } } private void ThreadSleepInSeconds(int seconds) { try { SECONDS.sleep(seconds); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "MyClass{" + "value1=" + value1 + ", value2=" + value2 + '}'; } } }
A result of execution:
2018-11-26T05:52:31.434Z : MyClass{value1=5, value2=7} 2018-11-26T05:52:32.437Z : MyClass{value1=5, value2=7} 2018-11-26T05:52:33.437Z : MyClass{value1=5, value2=7} 2018-11-26T05:52:34.440Z : MyClass{value1=5, value2=7}
Now 2 treads simultaneously can access different synchronized methods and manipulate them. Without interfering each other.
Last interesting example I wanna share with you is synchronizing by class, it will lock ALL instances of same class whenever a thread enters a synchronized block of code, e.g. keeps a lock.
import java.time.Instant; import java.util.Timer; import java.util.TimerTask; import static java.util.concurrent.TimeUnit.SECONDS; public class SynchronizedExample5 { public static void main(String[] args) { MyClass myClassInstance1 = new MyClass(); MyClass myClassInstance2 = new MyClass();//new code new Thread(() -> { myClassInstance1.setValue1(5); }).start(); new Thread(() -> { myClassInstance2.setValue1(7);//new code }).start(); new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Instant.now() + " : " + " Instance 1 : " + myClassInstance1);//new code System.out.println(Instant.now() + " : " + " Instance 2 : " + myClassInstance2);//new code } }, 0, 1000); } private static class MyClass { private Integer value1; public Integer getValue1() { return value1; } public void setValue1(Integer value1) { synchronized (MyClass.class) {//new code this.value1 = value1; ThreadSleepInSeconds(5); } } private void ThreadSleepInSeconds(int seconds) { try { SECONDS.sleep(seconds); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "MyClass{" + "value1=" + value1 + '}'; } } }
A result of execution:
2018-11-26T06:02:34.526Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:34.542Z : Instance 2 : MyClass{value1=null} 2018-11-26T06:02:35.525Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:35.525Z : Instance 2 : MyClass{value1=null} 2018-11-26T06:02:36.530Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:36.530Z : Instance 2 : MyClass{value1=null} 2018-11-26T06:02:37.532Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:37.532Z : Instance 2 : MyClass{value1=null} 2018-11-26T06:02:38.535Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:38.536Z : Instance 2 : MyClass{value1=null} 2018-11-26T06:02:39.537Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:39.537Z : Instance 2 : MyClass{value1=7} 2018-11-26T06:02:40.542Z : Instance 1 : MyClass{value1=5} 2018-11-26T06:02:40.542Z : Instance 2 : MyClass{value1=7}
In this interesting example, thread 1 accessed method setValue1() of myClassInstance1 and was keeping a lock for ALL instances of this class. Thats why thread 2 couldn’t access setValue1() method of myClassInstance2 until thread 1 will finish, even though they were manipulating different instances of the same class.