#
"synchronized" keyword in Java
This tutorial explains the usage of "synchronized" keyword in static methods.
Info
- The code marked with "synchronized" keyword can be run by 1 thread at a time ONLY.
- In case a class contains more than one static synchronized method, only one thread can execute one of these methods at the same time.
In my example, I start from a simple Spring Boot application created using Spring initializr.
Let's take a look at the following example.
Example #1 - "synchronized" keyword in static methods
I will create 2 thread types:
package com.exampe.java;
public class MyThreadAdd extends Thread {
Integer threadNumber;
public MyThreadAdd(Integer threadNumber) {
this.threadNumber = threadNumber;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
for (int i = 1; i <= 5; i++) {
try {
MyStaticCounter.add(1, this.threadNumber);
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
package com.exampe.java;
public class MyThreadSubtract extends Thread {
Integer threadNumber;
public MyThreadSubtract(Integer threadNumber) {
this.threadNumber = threadNumber;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
for (int i = 1; i <= 5; i++) {
try {
MyStaticCounter.subtract(1, this.threadNumber);
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
I will create a class which keeps the counter and which must be accessed by ONLY a thread at a time.
package com.exampe.java;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class MyStaticCounter {
static int counter = 0;
// Will lock the unique MyStaticCounter instance
public static synchronized void add(Integer value, Integer threadNumber) throws InterruptedException {
counter = counter + value;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mm.ss");
LocalTime today = LocalTime.now();
String timeString = today.format(formatter);
System.out.println("Time:"+timeString+"/ Thread #" + threadNumber + " /add/ Counter="+counter);
Thread.sleep(2000);
}
// Will lock the unique MyStaticCounter instance
public static synchronized void subtract(Integer value, Integer threadNumber)throws InterruptedException {
counter = counter - value;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mm.ss");
LocalTime today = LocalTime.now();
String timeString = today.format(formatter);
System.out.println("Time:"+timeString+"/ Thread #" + threadNumber + " /subtract/ Counter="+counter);
Thread.sleep(2000);
}
// // Will lock only the subtract2. This is not a good approach : when we are working with more instances it is not working well !!!
// public synchronized void subtract2(int value){
// counter = counter - value;
// }
}
Here is my main class :
package com.exampe.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DemoApplication.class, args);
System.out.println("--------------------------------------------");
MyThreadAdd t1 = new MyThreadAdd(1);
MyThreadSubtract t2 = new MyThreadSubtract(2);
MyThreadAdd t3 = new MyThreadAdd(3);
MyThreadSubtract t4 = new MyThreadSubtract(4);
t1.start();t2.start();t3.start();t4.start();
// Waiting for the threads to complete
t1.join(); t2.join(); t3.join();t4.join();
System.out.println("--------------------------------------------");
System.out.println("End of the execution");
}
}
When I run the code I get (is generally different at each run):
--------------------------------------------
Thread 3 is running...
Thread 1 is running...
Thread 2 is running...
Thread 4 is running...
Time:44.38/ Thread #3 /add/ Counter=1
Time:44.40/ Thread #1 /add/ Counter=2
Time:44.42/ Thread #4 /subtract/ Counter=1
Time:44.44/ Thread #2 /subtract/ Counter=0
Time:44.46/ Thread #4 /subtract/ Counter=-1
Time:44.48/ Thread #1 /add/ Counter=0
Time:44.50/ Thread #3 /add/ Counter=1
Time:44.52/ Thread #1 /add/ Counter=2
Time:44.54/ Thread #4 /subtract/ Counter=1
Time:44.56/ Thread #2 /subtract/ Counter=0
Time:44.58/ Thread #4 /subtract/ Counter=-1
Time:45.00/ Thread #1 /add/ Counter=0
Time:45.02/ Thread #3 /add/ Counter=1
Time:45.04/ Thread #1 /add/ Counter=2
Time:45.06/ Thread #4 /subtract/ Counter=1
Thread 1 stopped.
Time:45.08/ Thread #2 /subtract/ Counter=0
Thread 4 stopped.
Time:45.10/ Thread #3 /add/ Counter=1
Time:45.12/ Thread #2 /subtract/ Counter=0
Time:45.14/ Thread #3 /add/ Counter=1
Time:45.16/ Thread #2 /subtract/ Counter=0
Thread 3 stopped.
Thread 2 stopped.
--------------------------------------------
End of the execution
Info
As you can see there are no two or more threads executing MyStaticCounter.add
and MyStaticCounter.subtract
in the same time.
Take a look at the following new examples:
Example #2 - lock an object/variable in the class
[some code here]
synchronized(object) {
//some code here
}
[some code here]
In this case, when a thread execute this synchronized block, no other thread can have access to that object, which generally is a class variable.
If the object is "this" keyword, the lock will be on the method or on a part of it.
Example #3 - lock a method from an instance of class
public synchronized void add() {
// Code here ...
}
In this case, when a thread execute add()
method, no other thread can have access to this method,
but, for the same instance of the class.
The high-priority threads have more priority for synchronized block. It can prevent low-priority threads from obtaining the resources they need to execute, leading to Thread starvation.