Can threads execute different synchronized methods on same object?
In our earlier post, we learnt that when a method is synchronized only one thread will be allowed to enter the method. In this post, let’s do a little bit of deep dive – What would happen if an object has two synchronized methods? When a thread is executing the first synchronized method, will another thread be allowed to execute the second synchronized method?
Video: To see the visual walk-through of this post, click below:
Multiple synchronized methods in a same object example
To facilitate our study, I have put-together an interesting program. In this program, we are trying to simulate two threads trying to execute two synchronized methods of the same object, at the same point in time.
01: public class GirlFriend { 02: 03: public void sing() { 04: 05: try { 06: for(int i = 1; i <= 10; ++i) { 07: System.out.println("lullaby"); 08: Thread.sleep(100); 09: } 10: } catch (Exception e) { 11: } 12: } 13: 14: public void count() { 15: 16: try { 17: for(int i = 1; i <= 10; ++i) { 18: System.out.println(i); 19: Thread.sleep(100); 20: } 21: } catch (Exception e) { 22: } 23: } 24: }
This program has a ‘GirlFriend’ class. It contains two synchronized methods:
1. ‘sing()’ declared in line #3 which prints ‘lullaby’ 10 times.
2. ‘count()’ declared in line #14 which prints ‘1’ to ’10’.
We have put the thread to sleep for 100 milliseconds after printing in line #8 and #19, so that we can capture thread dumps during the program execution, which is needed for our discussion.
01: public class SameObjectSynchDemo { 02: 03: private static GirlFriend girlFriend = new GirlFriend(); 04: 05: private static class BoyFriend1 extends Thread { 06: 07: @Override 08: public void run() { 09: 10: girlFriend.sing(); 11: } 12: } 13: 14: private static class BoyFriend2 extends Thread { 15: 16: @Override 17: public void run() { 18: 19: girlFriend.count(); 20: } 21: } 22: 23: public static void main(String args[]) { 24: 25: new BoyFriend1().start(); 26: new BoyFriend2().start(); 27: } 28: }
This SameObjectSynchDemo class contains two threads:
a. ‘BoyFriend1’ thread which invokes ‘GirlFriend’ object’s ‘sing()’ method in line #10
b. ‘BoyFriend2’ thread which invokes ‘GirlFriend’ object’s ‘count()’ method in line #19
Both of these threads are launched concurrently in line #25, #26.
Synchronized methods execution output
When we executed the above program, we got following as output:
lullaby 1 lullaby 2 lullaby 3 lullaby 4 lullaby 5 lullaby 6 lullaby 7 lullaby 8 lullaby 9 lullaby 10
Fig: Output when sing() and count() is not synchronized
You can see that the ‘GirlFriend’ object is singing and counting at the same time i.e. you can notice that ‘lullaby’ and numbers are mixed up and printed. If you are going to ask the ‘GirlFriend’ object to sing and count at the same time, it’s going to be quite confusing. We need to help her. This is where the synchronized method comes to help.
Now, we made both ‘sing()’ and ‘count()’ as synchronized methods i.e., we introduced the ‘synchronized’ keyword in line #3 and line #14 of the ‘GirlFriend’ class. When we executed this modified program, we got following as output:
lullaby lullaby lullaby lullaby lullaby lullaby lullaby lullaby lullaby lullaby 1 2 3 4 5 6 7 8 9 10
Fig: Output when sing() and count() is synchronized
You can clearly see the difference in the output. When both ‘sing()’ and ‘count()’ are synchronized, ‘GirlFriend’ object first completed her singing and then only started counting i.e. ‘lullaby’ was printed first and then ‘1’ to ’10’ was printed later. This should make the ‘GirlFriend’ object really happy. This magic is only happening because of synchronization.
How do two Synchronized methods work in Java?
When a thread executes the synchronized method, it obtains the lock of the underlying object. Only one thread can hold the lock of an object at any given time. Since ‘BoyFriend1’ thread started first, when it entered the ‘sing()’ method, it acquired lock of the ‘GirlFriend’ object; when ‘BoyFriend2’ thread tries to enter the ‘count()’ method, it would try to acquire the lock of the same ‘GirlFriend’ object. Since ‘BoyFriend1’ thread is already holding on to the GirlFriend’s lock, ‘BoyFriend2’ thread will not be allowed to acquire the lock and it will be put to BLOCKED state. Only after the ‘BoyFriend1’ thread exits the ‘sing()’ method, it will release the GirlFriend’s lock. Only after that ‘BoyFriend2’ thread can enter the ‘count()’ method.
Threads behavior of Synchronized methods
To confirm this theory, we executed the above program and captured thread dump using open-source script yCrash. We analyzed the thread dump using the fastThread tool. Here is the generated thread dump analysis report of this simple program. Below is the excerpt from the thread dump analysis report:
Whenever threads are blocked, the fastThread tool will report them as a transitive graph. From the graph you can notice that ‘BoyFriend2’ is being blocked by ‘BoyFriend1’. (Note: This graph would make more sense especially when multiple threads are getting blocked. You can look at the introductory post where multiple threads got BLOCKED). When clicking on the thread names you can see its complete stack trace. Below are both threads stack trace:
BoyFriend1 Stack Trace is: java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep0(java.base@19.0.1/Native Method) at java.lang.Thread.sleep(java.base@19.0.1/Thread.java:465) at learn.synchornized.sameobject.GirlFriend.sing(GirlFriend.java:10) - locked <0x00000007141e3fe0> (a learn.synchornized.sameobject.GirlFriend) at learn.synchornized.sameobject.SameObjectSynchDemo$BoyFriend1.run(SameObjectSynchDemo.java:16) Locked ownable synchronizers: - None
Fig: BoyFriend1 thread stack trace showing it acquired the GirlFriend lock
BoyFriend2 Stack Trace is: java.lang.Thread.State: BLOCKED (on object monitor) at learn.synchornized.sameobject.GirlFriend.count(GirlFriend.java:19) - waiting to lock <0x00000007141e3fe0> (a learn.synchornized.sameobject.GirlFriend) at learn.synchornized.sameobject.SameObjectSynchDemo$BoyFriend2.run(SameObjectSynchDemo.java:25) Locked ownable synchronizers: - None
Fig: BoyFriend2 thread stack trace showing it got BLOCKED when trying to acquire the GirlFriend lock
You can notice that the ‘BoyFriend2’ thread is blocked, because it’s trying to acquire the lock ‘0x00000007141e3fe0’ (which is the object id of ‘GirlFriend’), when it’s trying to invoke the ‘count()’ method. On the other hand you can notice ‘BoyFriend1’ acquired the same lock ‘0x00000007141e3fe0’ and executed the ‘sing()’. ‘BoyFriend1’ wouldn’t release this lock until it completes executing the ‘sing()’ method. Thus the ‘BoyFriend2’ thread is stranded and put into BLOCKED state.
Conclusion
In this post, we learnt how JVM would behave when two threads try to execute different synchronized methods on the same object. You may also consider reading this post, where we attempt to explain how a JVM would behave when two threads try to execute the same synchronized method on different objects.
Published on Java Code Geeks with permission by Ram Lakshmanan, partner at our JCG program. See the original article here: Can threads execute different synchronized methods on same object? Opinions expressed by Java Code Geeks contributors are their own. |