Synchronization is a process that does not allow more than one thread for a resource at any time. In the java multi-threaded environment, multiple threads attempt to access the same resource, leading to incorrect and unforeseen outcomes. To prevent this from happening, Synchronization controls threads for sequential access to resources.
Java Thread Synchronization is a way to synchronise Java threads to access shared resources in a controlled manner. If multiple threads attempt to access a shared resource due to various concurrency problems, they may result in unintended outcomes. Java threads are synchronised to prevent unforeseeable outcomes. Only one thread will have access to the shared resource at a time
If a thread requires access to an object’s fields, it should lock the object before accessing it, and then release the lock when it’s done. Synchronized method is used to lock a shared resource object. If a thread invokes a synchronised method, it automatically receives the lock for that object and releases the lock after the code block execution has been completed.
Synchronization types
There are three types of thread synchronisation in java. These types will synchronise threads and execute each other exclusively.
- Synchronized block
- Synchronized method
- Static Synchronization
Threads without Synchronization
To understand Synchronization, let’s start with threads running parallel without synchronisation. Threads will run parallel and attempt to access the resource at the same time. The results are printed in an unpredictable order, as shown in the example below.
The order to execute the thread would be unpredictable. If you run the example below several times, you will see a different orders for thread execution. The execution of the thread depends on the allocation of the operating system or the java process cpu.
MyThread.java
package com.yawintutor;
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Thread name : " + getName() + " count : " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MyThreadMain.java
package com.yawintutor;
public class MyThreadMain {
public static void main(String[] args) throws Exception {
MyThread thread1 = new MyThread();
thread1.setName("THREAD 1");
thread1.start();
MyThread thread2 = new MyThread();
thread2.setName("THREAD 2");
thread2.start();
}
}
Output
Thread name : THREAD 1 count : 0
Thread name : THREAD 2 count : 0
Thread name : THREAD 1 count : 1
Thread name : THREAD 2 count : 1
Thread name : THREAD 2 count : 2
Thread name : THREAD 1 count : 2
Thread name : THREAD 2 count : 3
Thread name : THREAD 1 count : 3
Thread name : THREAD 2 count : 4
Thread name : THREAD 1 count : 4
Synchronized Threads
There is a controlled execution of the synchronised thread. Threads are formed one after the other to access the resource. In this example, the data is printed from one thread to another thread.
In the example below, an object created by the main method is used as a lock object. The reference to the lock object is passed and assigned to the local variable in both threads. If a thread executes the run method, the synchronised keyword will first lock the string object. Then the synchronised block starts to execute the statement. If the synchronised block is executed, the locked object will be released.
If another thread is trying to run the run method, the thread will wait until the locked object is released in a synchronised block. If the locked object is released, the synchronised keyword will lock the object and execute the synchronised block.
SyncThread.java
package com.yawintutor;
public class SyncThread extends Thread {
public String lockingObject;
public void run() {
synchronized (lockingObject) {
for (int i = 0; i < 5; i++) {
System.out.println("Thread name : " + getName() + " count : " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
SyncThreadMain.java
package com.yawintutor;
public class SyncThreadMain {
public static void main(String[] args) throws Exception {
String obj = "lockobject";
SyncThread thread1 = new SyncThread();
thread1.setName("THREAD 1");
thread1.lockingObject = obj;
thread1.start();
SyncThread thread2 = new SyncThread();
thread2.setName("THREAD 2");
thread2.lockingObject = obj;
thread2.start();
}
}
Output
Thread name : THREAD 1 count : 0
Thread name : THREAD 1 count : 1
Thread name : THREAD 1 count : 2
Thread name : THREAD 1 count : 3
Thread name : THREAD 1 count : 4
Thread name : THREAD 2 count : 0
Thread name : THREAD 2 count : 1
Thread name : THREAD 2 count : 2
Thread name : THREAD 2 count : 3
Thread name : THREAD 2 count : 4
Synchronized Threads using static lock
The static lock variable creates a class instance. The static variable holds the same object in all class instances. If the static variable is used in the synchronised keyword to lock the thread, all threads will wait until the lock is released.
SyncThread.java
package com.yawintutor;
public class SyncThread extends Thread {
public static String lockingObject="";
public void run() {
synchronized (lockingObject) {
for (int i = 0; i < 5; i++) {
System.out.println("Thread name : " + getName() + " count : " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
SyncThreadMain.java
package com.yawintutor;
public class SyncThreadMain {
public static void main(String[] args) throws Exception {
SyncThread thread1 = new SyncThread();
thread1.setName("THREAD 1");
thread1.start();
SyncThread thread2 = new SyncThread();
thread2.setName("THREAD 2");
thread2.start();
}
}
Output
Thread name : THREAD 1 count : 0
Thread name : THREAD 1 count : 1
Thread name : THREAD 1 count : 2
Thread name : THREAD 1 count : 3
Thread name : THREAD 1 count : 4
Thread name : THREAD 2 count : 0
Thread name : THREAD 2 count : 1
Thread name : THREAD 2 count : 2
Thread name : THREAD 2 count : 3
Thread name : THREAD 2 count : 4