Welcome to part 3 of Java Synchronization Tutorial series! In part 2, you know how to use Lock and Condition objects for synchronizing access to a method. This is basically how synchronized is designed and working in Java. And to make it easier for programmers, Java provides the synchronized keyword that operates on the default lock of a class. This default lock is called intrinsic lock which belongs to every Java object.

The synchronized keyword can be used at method level or code block level. Let’s look at the first approach first.

 

1. Synchronized Methods

Consider the following class:

public class A {
	public synchronized void update() {
		// code needs to be serialized for access
	}
}
Here, the update() method is synchronized. It is equivalent to the following code that uses a lock object explicitly:

public class A {
	public void update() {
		this.intrinsicLock.lock();

		try {

			// code needs to be serialized for access

		} finally {
			this.intrinsicLock.unlock();
		}
	}
}
Here, the intrinsic lock belongs to an instance of the class. And the following code explains how to use condition with a synchronized method:

public class A {
	public synchronized void update() {
		if (!condition) {
			this.wait();
		}

		// code needs to be serialized for access

		this.notify();

		// or:

		this.notifyAll();
	}
}
The methods wait(), notify() and notifyAll() behaves in the same manner as the methods await(), signal() and signalAll() of a Lock object. These methods are provided by the Object class. So every object has its own intrinsic lock and intrinsic condition.

Now, the Bank class can be rewritten using the synchronized keyword as follows:

/**
 * Bank.java
 * This class represents a bank that manages accounts and provides
 * money transfer function.
 * It demonstrates how to use the the synchronized keyword to serialize
 * access to methods.
 * @author www.codejava.net
 */
public class Bank {
	public static final int MAX_ACCOUNT = 10;
	public static final int MAX_AMOUNT = 10;
	public static final int INITIAL_BALANCE = 100;

	private Account[] accounts = new Account[MAX_ACCOUNT];

	public Bank() {
		for (int i = 0; i < accounts.length; i++) {
			accounts[i] = new Account(INITIAL_BALANCE);
		}
	}

	public synchronized void transfer(int from, int to, int amount) {
		try {
			while (accounts[from].getBalance() < amount) {
				wait();
			}

			accounts[from].withdraw(amount);
			accounts[to].deposit(amount);

			String message = "%s transfered %d from %s to %s. Total balance: %d\n";
			String threadName = Thread.currentThread().getName();
			System.out.printf(message, threadName, amount, from, to, getTotalBalance());

			notifyAll();

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized int getTotalBalance() {
		int total = 0;

		for (int i = 0; i < accounts.length; i++) {
			total += accounts[i].getBalance();
		}

		return total;
	}
}
You see, using the synchronized keyword make the code more compact, right? But you wouldn’t understand how a synchronized method works without understanding about the explicit locking mechanism, would you?



Let recompile the Bank class and then run the TransactionTest program again, you will see that the program behaves the same way as the previous version which uses explicit locking mechanism. But you write much less code. It’s cool, isn’t it?

The following are some noteworthy points with regards to synchronized instance methods (non-static ones):

 

2. Synchronized Blocks

In case you want to synchronize access at a smaller scope, i.e. a block of code rather than the whole method, you can use the synchronized keyword like this:

public void update() {
	synchronized (obj) {
		// code block
	}
}
A thread must hold the lock associated with the object obj before it can execute the code block. The obj can be any kind of object which you want to use it as a lock.

And use the synchronized block with condition as follows:

synchronized (obj) {
	if (!condition) {
		obj.wait();
	}

	// code block

	obj.notify();

	// or:

	obj.notifyAll();
}
By using synchronized blocks you have greater control over which part of the code should be serialized for access. For example, you can block concurrent access to the write() method while allow the read() method to be executed concurrently:

public class A {
	private Object lock = new Object();

	public void write() {
		synchronized (lock) {
			// code to write
		}
	}

	public void read() {
		// code to read
	}
}
Here, you can see a pure Object is used as a lock. The write() method can be executed by only one thread at a time, whereas the read() method can be executed by multiple threads concurrently. This is possible because when a thread is executing the synchronized block, it doesn’t necessarily have to own the lock associated with the instance of the class. Instead, it holds the lock associated with the object protected by the synchronized block.

Note that synchronizing a code block on the current instance of the class is equivalent to synchronizing an instance method. That means the following code:

public void update() {
	synchronized (this) {
		// code block
	}
}
is equivalent to this:

public synchronized void update() {
    // code block
}
Hence the Bank class can be rewritten using synchronized blocks on the this instance. It’s your exercise.

 

3. Synchronized Static Methods

You can synchronize a static method and for that the threads have to acquire a different lock: the lock associated with the class itself (static), not an instance of the class (this).

That means if you write:

public class A {

	public static synchronized void update() {
		// code
	}
}
is equivalent to:

public class A {

	public static void update() {
		synchronized (A.class) {
			// code
		}
	}
}
So when a thread is executing a synchronized static method, it also blocks access to all other synchronized static methods. The synchronized non-static methods are still executable by other threads. It’s because synchronized static methods and synchronized non-static methods work on different locks: class lock and instance lock.

In other words, a synchronized static method and a non-static synchronized method will not block each other. They can run at the same time.

That’s how the intrinsic (implicit) locking mechanism works in Java.

 

4. Explicit Locking vs. Intrinsic Locking

So far I have explained to you the work of two synchronization mechanism in Java:

Now the question is: when to use which? When to use Lock and when to use synchronized?

Here are some guidelines that help you make your decision:

Remember that using synchronized keyword is easier and less error-prone then using explicit lock. Using explicit lock gives you more control but you have to put more effort.

 

Related Tutorials:

 

Other Java Concurrency Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.