English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
When we start two or more threads in a program, it may be the case that multiple threads try to access the same resource, and as a result of concurrency issues, they may produce unpredictable results. For example, if multiple threads try to write data to the same file, they may damage the data because one thread can overwrite the data, or when one thread opens the same file, another thread may be closing the same file at the same time.
Therefore, it is necessary to synchronize the operations of multiple threads and ensure that only one thread can access the resources at a given time. This is achieved using a calledMonitorsEach object in Java is associated with a monitor, and threads can lock or unlock the monitor. Only one thread can hold the lock on a monitor at a time.
The Java programming language provides a very convenient way to create threads and implement the concept ofSynchronizationblock toSynchronizationIts task. You should keep the shared resources within this block. The general form of the synchronized statement is as follows.
synchronized(objectidentifier) { //access shared variables and other shared resources }
Here,objectidentifierIt is a reference to an object whose lock is associated with the monitor represented by the synchronized statement. Now, we will see two examples where two different threads will be used to print the counter. When the threads are not synchronized, they will not print the counter values in order, but when we place them insidesynchronized()
When printing the counter in the block, both threads will print the counter in order in a very large amount.
This is a simple example that can print the counter value in order, or not in order, and it will produce different results each time it is run based on the CPU availability of the thread.
class PrintDemo extends Thread { public void printCount() { try { for(int i = 5; i > 0; i--) { System.out.println("Counter --- " + i ); } } catch (Exception e) { System.out.println("Thread " + Thread.currentThread().getName()+" interrupted."); } } public void run() { printCount(); System.out.println("Thread " + Thread.currentThread().getName() + " exiting."); } } public class TestThread { public static void main(String args[]) { PrintDemo PD = new PrintDemo(); Thread t1 = new Thread(PD); Thread t2 = new Thread(PD); t1.start(); t2.start(); //Waiting for the thread to end try { t1.join(); t2.join(); } catch (Exception e) { System.out.println("Interrupted"); } } }
Different results will be produced each time you run the program.
Output Result
Counter --- 5 Counter --- 5 Counter --- 4 Counter --- 4 Counter --- 3 Counter --- 3 Counter --- 2 Counter --- 2 Counter --- 1 Counter --- 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
This is the same example, which prints the counter values in order and produces the same result each time it is run. We have placed the synced keyword on a block so that now the counter increment code is locked during method execution based on the object. We use the current object as the lock and pass the synchronized block as a parameter.
class PrintDemo extends Thread { public void printCount() { try { for(int i = 5; i > 0; i--) { System.out.println("Counter --- " + i ); } } catch (Exception e) { System.out.println("Thread " + Thread.currentThread().getName()+" interrupted."); } } public void run() { synchronized(this) { printCount(); } System.out.println("Thread " + Thread.currentThread().getName() + " exiting."); } } public class TestThread { public static void main(String args[]) { PrintDemo PD = new PrintDemo(); Thread t1 = new Thread(PD); Thread t2 = new Thread(PD); t1.start(); t2.start(); //Waiting for the thread to end try { t1.join(); t2.join(); } catch (Exception e) { System.out.println("Interrupted"); } } }
The same result will be produced each time you run the program.
Output Result
Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread-2 exiting. Thread Thread-1 exiting.
This is the same example, which prints the counter values in order and produces the same result each time it is run. This time we have placed the synced keyword on a method to lock the entire method during method execution based on the object.
class PrintDemo extends Thread { public void printCount() { try { for(int i = 5; i > 0; i--) { System.out.println("Counter --- " + i ); } } catch (Exception e) { System.out.println("Thread " + Thread.currentThread().getName()+" interrupted."); } } public synchronized void run() { printCount(); System.out.println("Thread " + Thread.currentThread().getName() + " exiting."); } } public class TestThread { public static void main(String args[]) { PrintDemo PD = new PrintDemo(); Thread t1 = new Thread(PD); Thread t2 = new Thread(PD); t1.start(); t2.start(); //Waiting for the thread to end try { t1.join(); t2.join(); } catch (Exception e) { System.out.println("Interrupted"); } } }
The same result will be produced each time you run the program.
Output Result
Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread-1 exiting. Counter --- 5 Counter --- 4 Counter --- 3 Counter --- 2 Counter --- 1 Thread Thread-2 exiting.