Core Java

Java Static Synchronized method behavior

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 discuss the behavior of static synchronized methods.


Video: To see the visual walk-through of this post, click below:


Multiple static 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 static synchronized methods at the same point in time.

01: public class GirlFriend {

02: 

03:    public static synchronized 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 static synchronized 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 static synchronized methods: 

 1. ‘sing()’ static method declared in line #3 which prints ‘lullaby’ 10 times.

 2. ‘count()’ static method 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 StaticSynchDemo {

02:    

03:    private static class BoyFriend1 extends Thread {

04:       

05:       @Override

06:       public void run() {

07:          

08:          GirlFriend.sing();

09:       }

10:    }

11:    

12:    private static class BoyFriend2 extends Thread {

13:          

14:       @Override

15:       public void run() {

16:          

17:          GirlFriend.count();

18:       }

19:    }

20:    

21:    public static void main(String args[]) {

22:       

23:       new BoyFriend1().start();

24:       new BoyFriend2().start();

25:    }

26: }

This StaticSynchDemo class contains two threads:

a. ‘BoyFriend1’ thread which invokes ‘GirlFriend’ class’s static synchronized ‘sing()’ method in line #8

b. ‘BoyFriend2’ thread which invokes ‘GirlFriend’ class’s static synchronized ‘count()’ method in line #17

Both of these threads are launched concurrently in line #23, #24.

Static Synchronized methods execution output

When we executed the above 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 static synchronized

You can see that the ‘GirlFriend’ class first completes singing and then starts the counting even though both the ‘BoyFriend1’ and ‘BoyFriend2’ threads try to invoke the ‘GirlFriend’ class at same time. This magic is happening only because of synchronization. 

How do two static Synchronized methods work in Java?

When a thread executes the static synchronized method, it obtains the lock of the java.lang.Class of that particular object (i.e., GirlFriend’s class object). Only one thread can hold the lock of the class object at any given time. Since the ‘BoyFriend1’ thread started first, when it entered the ‘sing()’ method it acquired the ‘GirlFriend class’ object lock. At this time, ‘BoyFriend2‘ thread tries to enter the ‘count()’ method and it will try to acquire the lock of the same ‘GirlFriend class’ object. Since the ‘BoyFriend1’ thread is already holding on to the GirlFriend’s class lock, the ‘BoyFriend2’ thread will not be allowed to acquire the lock and it will be put to the BLOCKED state. Only after the ‘BoyFriend1’ thread exits ‘sing()’ method, it will release the GirlFriend’s class lock, only after that the ‘BoyFriend2’ thread will enter the ‘count()’ method.

Thread behavior of static 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:

Fig: fastThread tool reporting 1 thread is in BLOCKED state
Fig: Transitive graph showing ‘BoyFriend2’ blocked by ‘BoyFriend1’ (generated by fastThread)

Whenever threads are blocked, 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:

01: BoyFriend1 Stack Trace is:

02: java.lang.Thread.State: TIMED_WAITING (sleeping)

03: at java.lang.Thread.sleep0(java.base@19.0.1/Native Method)

04: at java.lang.Thread.sleep(java.base@19.0.1/Thread.java:465)

05: at learn.synchornized.stat.GirlFriend.sing(GirlFriend.java:10)

06: - locked <0x0000000713e71e00> (a java.lang.Class for learn.synchornized.stat.GirlFriend)

07: at learn.synchornized.stat.StaticSynchDemo$BoyFriend1.run(StaticSynchDemo.java:14)

08: Locked ownable synchronizers:

09: - None

Fig: BoyFriend1 thread stack trace showing it acquired the GirlFriend class lock in line #6

01: BoyFriend2 Stack Trace is:

02: java.lang.Thread.State: BLOCKED (on object monitor)

03: at learn.synchornized.stat.GirlFriend.count(GirlFriend.java:19)

04: - waiting to lock <0x0000000713e71e00> (a java.lang.Class for learn.synchornized.stat.GirlFriend)

05: at learn.synchornized.stat.StaticSynchDemo$BoyFriend2.run(StaticSynchDemo.java:23)

06: Locked ownable synchronizers:

- None

Fig: BoyFriend2 thread got BLOCKED when trying to acquire the GirlFriend class lock in line #4

You can notice that the ‘BoyFriend2’ thread is blocked (line #4), because it’s trying to acquire the lock ‘0x0000000713e71e00’ (which is the object id of ‘GirlFriend class’) when it’s trying to invoke the ‘count()’ method. On the other hand, you can notice that the ‘BoyFriend1’ thread acquired the same lock ‘0x0000000713e71e00’ and executed the ‘sing()’ method. ‘BoyFriend1’ thread wouldn’t release this lock until it completes executing the ‘sing()’ method. Thus, ‘BoyFriend2’ thread is stranded and put into the BLOCKED state.

Static Synchronized and non-static Synchronized method

To make our study a little bit more interesting, we will try to understand the behavior when a static synchronized method and non-static synchronized method is concurrently invoked at the same point in time. To facilitate this study, I have modified the above program slightly. 

01: public class GirlFriend {

02: 

03:    public static synchronized 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 synchronized 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 methods: 

 1. ‘sing()’ static synchronized method declared in line #3 which prints ‘lullaby’ 10 times.

 2. ‘count()’ synchronized method 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 StaticNonStaticSynchDemo {

02:    

03:    private static class BoyFriend1 extends Thread {

04:       

05:       @Override

06:       public void run() {

07:          

08:          GirlFriend.sing();

09:       }

10:    }

11:    

12:    private static class BoyFriend2 extends Thread {

13:          

14:       private GirlFriend myGirlFriend = new GirlFriend();

15:       

16:       @Override

17:       public void run() {

18:          

19:          myGirlFriend.count();

20:       }

21:    }

22:    

23:    public static void main(String args[]) {

24:       

25:       new BoyFriend1().start();

26:       new BoyFriend2().start();

27:    }

28: }

This ‘StaticNonStaticSynchDemo’ class contains two threads:

a. ‘BoyFriend1’ thread which invokes ‘GirlFriend’ class static synchronized ‘sing()’ method in line #8

b. ‘BoyFriend2’ thread which invokes ‘GirlFriend’ object’s synchronized ‘count()’ method in line #19

Both of these threads are launched concurrently in line #25, #26.

Static Synchronized & non-static synchronized methods execution output

 When we executed the above program, we got following as output:

lullaby

1

2

lullaby

lullaby

3

lullaby

4

lullaby

5

lullaby

6

7

lullaby

lullaby

8

lullaby

9

lullaby

10

Fig: Output when invoking static synchronized sing() method and non-static synchronized count() method

From the output, you can notice that the ‘GirlFriend’ is singing and counting simultaneously. i.e., you can see that the ‘lullaby’ and numbers are printed in a mixed up manner. Even though methods are synchronized, why this sort of mixed up behavior happening? 

How does JVM execute static synchronized & non-static synchronized methods?

When a thread executes the static synchronized method, it obtains the lock of the java.lang.Class of that particular object (i.e., ‘GirlFriends class’ object). On the other hand, when a thread executes a non-static synchronized method, it obtains the lock of that object itself (i.e, ‘GirlFriend’ object). Thus, in the above example, ‘BoyFriend1’ thread acquires the lock of ‘GirlFriend class’ when it enters ‘sing()’ method, while ‘BoyFriend2’ thread acquires the lock of the ‘GirlFriend’ object when it enters ‘count()’ method. These are two different locks. Thus, both the ‘BoyFriend1’ thread and ‘BoyFriend2’ thread can execute the ‘sing()’ and ‘count()’ methods simultaneously. That’s the reason why you are seeing the ‘lullaby’ string the numbers to be mixed up and printed.

Threads behavior of static 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:

Fig: fastThread tool reporting no BLOCKED state threads

Fig: fastThread tool reporting no BLOCKED state threadsIn the earlier example, you would have noticed that the BLOCKED thread count printed as 1, whereas in this thread dump analysis report, there are no BLOCKED threads reported. This confirms that the ‘BoyFriend1’ and ‘BoyFriend2’ threads are running concurrently without blocking each other. 

Conclusion

In this post, we learnt in detail how static synchronized methods would work. To learn more about Java concurrency you may refer here.

Published on Java Code Geeks with permission by Ram Lakshmanan, partner at our JCG program. See the original article here: Java Static Synchronized method behavior

Opinions expressed by Java Code Geeks contributors are their own.

Ram Lakshmanan

Ram Lakshmanan developed world's finest DevOps tools: GCeasy.io, fastThread.io, HeapHero.io. Every single day, millions & millions of people in North America—bank, travel, and commerce—use the applications that Ram Lakshmanan has architected. Ram is an acclaimed speaker in major conferences on scalability, availability, and performance topics. Recently, he has founded a startup, which specializes in troubleshooting performance problems.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button