Learnitweb

synchronized in Java

An object in Java has a state and behavior. The state of the object is the value of the instance variables it has. The methods in the object are used to change the state of the object by changing the state of the object.

In multi-threaded programming, it is possible by multiple threads to invoke the same method and thus change the state of the object simultaneously. When multiple threads work on the same block of code responsible to change the state of the object, the results may be erroneous.

Every sensitive block of code which should be treated at atomic should be synchronized. Locking can guarantee both visibility and atomicity.

Synchronized blocks in Java are marked with the synchronized keyword.

Syntax

If we want to make a block of code as atomic, we can synchronize that block of code. Following is the syntax of a synchronized block:

synchronized (reference to the object) {
// block of code to be synchronized
} 

A synchronized block has two parts:

  1. Reference to the object which will act as a lock.
  2. Block of code to be synchronized.

Intrinsic lock

Every Java object can act as a lock. These are built-in locks and are called intrinsic locks or monitor locks. Intrinsic locks are mutually exclusive in nature, that is no two threads can acquire the same lock at the same time.

Whenever a thread enters a synchronized block, it automatically acquires a lock before entering the synchronized block. The lock is released when thread exits the synchronized block. If a thread A owns the thread, thread B can not acquire the same lock until thread A releases the thread. Since no two threads can acquire the same thread, the block of code act as atomic operation. There is only one lock per object, if one thread has acquired the lock, no other thread can acquire the lock until the first thread releases the lock.

Synchronized methods

Methods can be synchronized. When a method is synchronized, the block of code to be synchronized is the whole method.

When we enter a synchronized non-static method, we automatically acquire the lock associated with the current instance (the this instance) of the class.

Best practice: Every mutable state variable that is shared and can be accessed by more than one thread, must be guarded and must be guarded by the same acquired lock.

Problem without synchronization

In the following code, we are creating two thread. Each thread loops for five times and print the value of count. The desired result is to run only one thread at a time. But since there is no synchronization, both threads execute the same block of code and the result is not as required.

package com.learnitweb;

public class WithoutSynchronization implements Runnable {

	int count = 0;

	@Override
	public void run() {
		try {
			for (int i = 0; i < 5; i++) {
				count = count + 1;
				System.out.println(Thread.currentThread().getName() + ":" + count);
				Thread.sleep(2000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String args[]) {
		WithoutSynchronization myRunnable = new WithoutSynchronization();
		Thread thread1 = new Thread(myRunnable);
		Thread thread2 = new Thread(myRunnable);

		thread1.start();
		thread2.start();
	}
}

Output

Thread-1:2
Thread-0:2
Thread-0:3
Thread-1:4
Thread-0:5
Thread-1:6
Thread-0:7
Thread-1:8
Thread-0:9
Thread-1:10

Synchronized example

In this code, we are synchronizing the code. Any of the thread will acquire the lock and will loop and print values. We have used sleep method, so thread will not release the lock. The other thread will have to wait until first one releases the lock.

package com.learnitweb;

public class SynchronizationExample implements Runnable {

	int count = 0;

	@Override
	public void run() {
		try {
			synchronized (this) {
				for (int i = 0; i < 5; i++) {
					count = count + 1;
					System.out.println(Thread.currentThread().getName() + ":" + count);
					Thread.sleep(2000);
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String args[]) {
		SynchronizationExample myRunnable = new SynchronizationExample();
		Thread thread1 = new Thread(myRunnable);
		Thread thread2 = new Thread(myRunnable);

		thread1.start();
		thread2.start();
	}
}

Output

Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10

Synchronized Block Reentrance

If a thread holds a lock, and tries to invoke another synchronize method of the same object, it can do so.

Intrinsic locks are reentrant in nature. That is if a thread tries to acquire the lock it already holds, it can do so successfully. So we can say that locks are acquired on a per thread basis.

Summary and points to remember

  • Methods or block of code can be synchronized. If there is a part of method or block of code which is critical(in terms of changing state), you can synchronize the block of code. There is no need to synchronize the whole method.
  • Variables or classes can not be synchronized.
  • A class can have both synchronized and non-synchronized methods.
  • Only those methods need to be synchronized which access and change the state (variables) of the class such that if not done properly could give incorrect results.
  • Only one thread can acquire a lock on a object. The other thread has to wait until first thread returns the lock.
  • The object which is used to invoke the synchronized method is the object on which the lock is acquired.
  • If a class has both synchronized and non-synchronized methods then other threads can still access non-synchronized methods.
  • If a thread acquires a lock on an object and tries to access another synchronized method of the same class, it can do so successfully.
  • If a thread acquires a lock on an object and tries to access another synchronized method of another class, it can do so if the lock is available to be acquired.