|
Declaring the entire method as synchronized is simplest way of ensuring a single thread access the critical code. However, sometimes only a part of method is critical and needs protection from simultaneous access of multiple threads. For example, in your makePayment method, if you are downloading the payment history, you may not want it synchronized as you are not modifying anything in it. You only wish to make the actual makePayment part as synchronized. Java also allows you to synchronize only some of the statements of a method in a code block. Therefore you can write the makePayment() method as:
private void makePayment(int payAmount) {
// check user preferences
// download the payment
history
// now the actual payment logic starts..
synchronized(account) {
if (account.getAmountDue() > 0) {
System.out.println(...
. . .
}
}
}
The code block starts with synchronized(account) at the top. It tells the thread executing the makePayment method that the enclosed statements in this block needs to be have a synchronized access to the account object.
Note that every synchronized code is always synchronized with some object. It means that it need to get some object’s permission to modify (by acquiring its lock). This object provides the context for synchronization. In case of synchronized method, it is simple. The object used to invoke the method is implicitly assumed to be the synchronization context. Therefore, you need to acquire its lock before invoking that method. But when you synchronize a block of code, you can explicitly specify which object’s lock you want to acquire before executing the statements in that block. In our example, the statements within code block are going to modify account object, hence its synchronization context is the account object. You can also synchronize a code block on the same object in which it is present as:
synchronized(this) {
}
You should use the synchronized blocks instead of synchronizing entire method wherever you only want a part of it to be synchronized. Declaring the entire method makePayment() as synchronized will also work logically same. However performance-wise declaring the entire method synchronized is not as efficient as declaring a synchronized block. Synchronization does hurt the advantage of concurrent access in multithreading. Therefore, you should not synchronize any more code than necessary to protect your data. So if the scope of a method is more than required, its better reduce the scope of the synchronized part to just a block instead of entire method. Note however that you should not shy away from declaring the entire method as synchronized when all the statements in it need to be executed as single indivisible step.