|
The solution is actually quite simple. We have two steps while paying the account that are critical.
1. Checking that the amount is indeed due (amountDue > 0)
2. The actual payment of bill (account.payBill())
You need to make sure that the two steps of are never split apart. They must be executed as one indivisible step.
1. Checking that the amount is indeed due (amountDue > 0) and make the actual payment of bill. (account.payBill())
In other words, we want to make sure that once a thread starts executing the steps for making payment, no other thread can start executing them until the first thread is finished. Java allows us to mark these special steps with a synchronized keyword. In our example, once a thread starts executing the makePayment() method of UtilityBillManager, no other thread should enter it. Therefore, we can mark it as synchronized
private synchronized void makePayment(int payAmount) {
// method definition is same as in listing 12.6
}
By declaring that it is synchronized, this method declares that it is modifying a shared Object and hence only one thread can execute it at a time. Therefore, if Mickey thread happens to enter it first, Pluto thread could not enter it until Mickey is finished executing it. It cannot execute it even when Mickey thread is sleeping within this method. Thus this method is executed as one atomic unit of execution.
Remember that you cannot guarantee that a Mickey thread will stay running throughout the entire method execution. However, you can assure that even when the thread (that is running the synchronized method) moves in and out of the running state, no other thread will be able to execute it. In our example, if Mickey falls asleep after checking that amount is indeed due, it enters sleeping state. Meantime Pluto is refrained from making the bill payment (entering in makePayment()) until after Mickey wakes up and completes payment of bill. Therefore, if we run the ThreadTester after making the makePayment() as synchronized, the output will be something like this..
Mickey is going to pay the bill
Mickey paid the bill
Pluto, the bill is already paid
As you may notice that now, the thread Pluto will enter and execute the run method in parallel with the thread Mickey, but it cannot enter makePayment() until the thread Mickey is paying the bill because it is synchronized. Therefore it has to wait. Eventually when the thread Mickey has finished, the Pluto thread enters the makePayment() and finds that the bill is already paid. Thus declaring a critical method synchronized causes it to function correctly in multithreaded environment.