We can create synchronized (thread safe) blocks and methods in Java using the synchronized keyword. The method or block marked as synchronized can be accessed a single thread at a time.
What is the need for syncrhonized blocks and methods in Java?
In multithreaded environments, sometimes two threads access the same method or another code block simultaneously, which can lead to an issue.
Imagine a method that first checks if the record is present in DB. If not, it saves it. Otherwise, it ignores it.
Now, if you have multiple threads, one thread can enter the method, see that record is not present and it saves it, and at the same time another thread tries the same thing, you can end up in the situation where two threads try to save the same record, and you can get an exception.
To avoid the such race conditions, we can mark the method or a code block with a synchronized keyword.
How synchronized works?
Synchronized blocks or methods prevent thread interference. When one thread wants to access a method, it needs to acquire the lock on the monitoring object.
Once it gets it, no other thread can receive it until the current thread which holds the lock finishes with the execution of the synchronized method or block.
Then, other threads will wait until the lock can be acquired. In that way, the synchronized keyword assures that only one thread can execute the synchronized block at a time.
As we said, the synchronized keyword can be applied to methods and blocks.
Synchronized methods in Java
Both static and instance methods can be marked as synchronized.
When we make an instance method synchronized, only one thread per instance can execute the code inside it.
Declaring the synchronized instance method:
synchronized void method() {}
Synchronized method example
Let’s see what happens if two threads access the same non-synchronized method simultaneously:
public class SynchronizedInstanceMethodExample implements Runnable { public static void main(String[] args) { SynchronizedInstanceMethodExample runnable = new SynchronizedInstanceMethodExample(); new Thread(runnable).start(); // creates one thread new Thread(runnable).start(); // creates second thread } // Inherited run method from the Runnable interface @Override public void run() { try { // Calling the non-synchronized method print() print(); } catch (InterruptedException e) { System.out.println("Error occurred while running the print method using multiple threads..."); } } // non-synchronized method void print() throws InterruptedException { System.out.println("Thread enters the method: " + Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("Thread finishes executing: " + Thread.currentThread().getName()); } }
We implemented the Runnable interface to execute the code using multiple threads. Inside the print() method, we print what thread is currently executing the method and put some sleep to pause the current thread.
When we run the above program, we can see the following output:
Thread enters the method: Thread-1 Thread enters the method: Thread-2 Thread finishes executing: Thread-1 Thread finishes executing: Thread-2
You see that both methods entered the print() method. The Thread-2 didn’t wait for the Thread-1 to finish execution.
Now, let’s make the print() method synchronized and see the output:
//synchronized method void print() throws InterruptedException { System.out.println("Thread enters the method: " + Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("Thread finishes executing: " + Thread.currentThread().getName()); }
Synchronized blocks in Java
We don’t need to synchronize a whole method if we don’t need to. We can just synchronize a code block inside it.
Example
// non-synchronized method void print() throws InterruptedException { System.out.println("Thread enters the method: " + Thread.currentThread().getName()); synchronized (this) { // synchronized code block System.out.println("Thread enters the code block: " + Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("Thread finishes executing the code block: " + Thread.currentThread().getName()); } System.out.println("Thread finishes executing the method: " + Thread.currentThread().getName()); }
Happy coding!