While the synchronized method restricts access to only one thread at a time, it does not promise the output order if the method's operations are not in sequence or if the method does not explicitly manage the execution order.
Hence, to ensure synchronization of behavior, utilizing an instance-level lock is insufficient. Instead, a class-level lock is required, which can be implemented through static synchronization.
In essence, when you designate a static method as synchronized, the synchronization will be at the class level rather than the object level. This means that only one thread can execute operations on that method due to the class-level lock. To illustrate this concept, let's examine the scenario below.
Consider a scenario where a class contains several static synchronized methods (such as demo1, demo2, demo3, demo4). If one thread is currently accessing the method demo1, it will prevent any other thread from accessing any other static synchronized methods simultaneously.
Syntax:
static synchronized returnType methodName(Type parameters) {
// code
}
Why static synchronization?
Consider having two instances of a class called Table, denoted as object1 and object2. When utilizing synchronized methods or synchronized blocks, interference between object1 and object2, or any other pair like object3 and object4, is prevented. This is due to object1 and object2 pointing to the same object, which possesses a singular lock.
Interference may occur between t1 and t3 or t2 and t4 when t1 acquires a different lock than t3, or t2 acquires a different lock than t4. To prevent interference between these threads, static synchronization is implemented to address this issue.
Therefore, static synchronization is utilized to ensure that a shared resource is accessed in a manner that is safe for multiple threads.
Static Synchronization Example
Example
class Print
{
// static synchronized method declaration
public static synchronized void printMessage(String s)
{
for(int i = 0; i < 4; i++)
{
System.out.print("Good Night: ");
System.out.println(s);
try{
Thread.sleep(1000); //sleep thread for 1 sec
}
catch(InterruptedException e)
{
}
}
}
}
class MyThrd extends Thread {
Print p;
String s;
MyThrd(Print p, String s)
{
this.p = p;
this.s = s;
}
public void run()
{
p.printMessage(s);
}
}
public class Main {
public static void main(String arg[])
{
Print p1 = new Print();
Print p2 = new Print();
MyThrd th1 = new MyThrd(p1,"Mahi");
MyThrd th2 = new MyThrd(p2,"Sachin");
th1.start(); // start thread th1
th2.start(); // start thread th2
}
}
Output:
Good Night: Mahi
Good Night: Mahi
Good Night: Mahi
Good Night: Mahi
Good Night: Sachin
Good Night: Sachin
Good Night: Sachin
Good Night: Sachin
Good Night: Mahi
Good Night: Mahi
Good Night: Mahi
Good Night: Mahi
Good Night: Sachin
Good Night: Sachin
Good Night: Sachin
Good Night: Sachin
In the program provided, there are three classes: Print, MyThrd, and Main. Each of these classes is defined with its own set of properties and methods.
The Print class contains the necessary code for the child thread to execute.
The purpose of the MyThrd class is to inherit from the Thread class, set values to p (from the Print class) and s (from the String class), and call the printMessage function of the Print class.
The "Main" class serves as the primary class within the entire program, responsible for initiating a child thread.
Within the control flow of a program, the initial point of execution is the main method. Initially, two child threads are generated and linked to the print objects for those threads. Upon the activation of the th2.start instruction, the total count of threads will be three (main, th1, th2). The sequence of execution unfolds as detailed below.
Upon initiation, the child thread th1 commences its operation. Since the print method is synchronized as static, th1 obtains the class-level lock of the Print class and begins the execution of the printMessage method. Subsequently, any following thread, such as th2 in this scenario, must wait until the preceding thread has completed its execution to acquire the class-level lock.
Static Synchronization by Using the Anonymous Class
In this instance, a thread is being created using an unnamed class.
Example
//Creating a class that contains a static synchronized method
class Table{
synchronized static void printTable(int n){
for(int i=1;i<=10;i++){
System.out.println(n*i);
try{
Thread.sleep(100);
}catch(Exception e){}
}
}
}
//Creating a Main class to call static synchronized method on threads run
public class Main {
public static void main(String[] args) {
Thread t1=new Thread(){
public void run(){
Table.printTable(1);
}
};
Thread t2=new Thread(){
public void run(){
Table.printTable(10);
}
};
Thread t3=new Thread(){
public void run(){
Table.printTable(100);
}
};
Thread t4=new Thread(){
public void run(){
Table.printTable(1000);
}
};
t1.start();
t2.start();
t3.start();
t4.start();
}
}
Output:
100
200
300
400
500
600
700
800
900
1000
10
20
30
40
50
60
70
80
90
100
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
1
2
3
4
5
6
7
8
9
10
Synchronized block on a class lock:
The block synchronizes on the lock of the object denoted by the reference .class name .class. A static synchronized method printTable(int n) in class Table is equivalent to the following declaration:
static void printTable(int n) {
synchronized (Table.class) { // Synchronized block on class A
// ...
}
}
Difference Between Synchronization and Static Synchronization
| Aspect | Synchronization | Static Synchronization |
|---|---|---|
| Scope of Lock | It locks the instance (object level). | It locks the class object (class level) |
Use |
It applies to instance methods or blocks. | It applies to static methods or blocks. |
| Lock Context | Lock is associated with the current object instance. | The lock is related to the .class object of the class. |
| Thread Access | It ensures that only one thread accesses the synchronized instance code of the same object at a time. | It ensures only one thread accesses the synchronized static code of the entire class at a time. |
| Impact | It affects thread safety at the instance level. | It affects thread safety for shared static resources. |
Uses |
To synchronize instance-specific data access. | To synchronize shared data across all instances. |
| Declaration | synchronized void instanceMethod() | static synchronized void staticMethod() |
Uses |
It is commonly used. | It is not widely used. |
| Instances | In synchronization, for each object, a new instance is created. | In static synchronization, for the entire program, there is only one instance. |
Important Points to Remember
- It provides a lock at the class level, not for individual objects.
- At a time, only one thread can execute the synchronized static method or block.
- All instances of the class share the same lock.
Conclusion
In the preceding conversation, it was noted that a static synchronized method will acquire a lock on the class rather than the object. This occurs because the presence of the keyword static signifies that the lock is obtained on the class itself instead of an instance of the class.
The term "synchronized" indicates that the method can only be accessed by one thread at any given moment.
In the context of programming, "static synchronized" implies that exclusive access to the class is granted to a single thread at any given time.
Java Static Synchronization MCQs
- How can we achieve class-level lock?
- Using Static Synchronization
- Using Synchronization
- Using Multithreading
- Using
Explanation: When we declare a static method as synchronized, the lock will be on the class, not on the object. Since we can achieve the class-level lock, only one thread can do its operation on that method.
- Static synchronization can be applied to?
- Only with static methods
- Static methods and static block
- With Multithreading
- With Multithreading and static block
Explanation: Static synchronization can be applied to static methods and static blocks only.
- What are the valid static synchronization syntaxes?
- static synchronized returnType methodName(Type parameters) { }
- synchronized static returnType methodName(Type parameters) { }
- public static synchronized returnType methodName(Type parameters) { }
- public synchronized returnType methodName(Type parameters) { }
- Only i
- Only i and ii
- Only ii and iii
- Only i, ii, and iii
Explanation: We can declare a static synchronized method by using the i, ii, and iii statements.
- Which keyword is used to define a static synchronized method??
- volatile
- final
- synchronized
- transient
Explanation: Java provides the synchronization keyword to achieve synchronization.
- What does static synchronization ensure in Java?
- Instance-level Synchronization
- Class-level Synchronization
- Object-level Synchronization
- Thread-level Synchronization
In the context of synchronization, static synchronization guarantees synchronization at the level of the class.