The effects of synchronization on the threads local cache

why I said earlier that its useful to think of synchronization information as belonging to the stack frames. Methods can also be declared as synchronized methods. Declaring a method as synchronized is equivalent to placing it inside a single synchronized block, which is synchronized on this . The following code is equivalent to the previous example that used synchronizedthis : public synchronized void makeWithdrawalMoney amount throws RemoteException, OverdraftException, NegativeAmountException { checkForNegativeAmountamount; checkForOverdraftamount; _balance.subtractamount; return; } Synchronization is not part of the method signature. A method can be synchronized in a superclass and not synchronized in a subclass. Similarly, methods declared in an interface cannot be declared synchronized. Theres a very important point here: synchronization only affects the code in synchronized blocks or inside a synchronized method. Suppose, for example, we synchronize two out of the three public methods in Account_Impl , as in the following declarations: public Money getBalance throws RemoteException public synchronized void makeDepositMoney amount throws RemoteException, NegativeAmountException public synchronized void makeWithdrawalMoney amount throws RemoteException, OverdraftException, NegativeAmountException This has the following effects: • Any number of threads can be executing the getBalance method at any time. There are no restrictions whatsoever on the number of threads that can execute getBalance . • At most, one thread can be executing either the makeDeposit or makeWithdrawal methods. • If a thread is executing makeDeposit , then no thread can execute make - Withdrawal . • If a thread is executing makeWithdrawal , then no thread can execute make - Deposit .

11.4.1.1 The effects of synchronization on the threads local cache

Synchronizing on an object also affects a threads local cache. Any time a thread synchronizes on an object, its cache is partially invalidated. That is, the first time a thread accesses a variable after acquiring a lock, it must load or reload that variable from main memory. Unlocking has a similar effect. Any time a thread releases a lock, any variables in its local cache it has changed since acquiring that particular lock must be flushed to the main heap. It is important to note that this flushing occurs before the lock is released. Unfortunately, this isnt quite as powerful as you might think. If two threads synchronize on different objects, then they might interfere with each other. Suppose, for example, we implemented a simple logging facility in our bank example. All this does is increment a static variable that tells us the number of transactions we handled: public class Account_Impl extends UnicastRemoteObject implements Account { private static int _numberOfTransactions = 0; private Money _balance; public syncrhonized void makeDepositMoney amount throws RemoteException, NegativeAmountException { checkForNegativeAmountamount; _balance.addamount; _numberOfTransactions++; return; } Suppose two customers made deposits to different accounts. We know that: • Customer 1 synchronized on her account and then accessed _numberOfTransactions before releasing the lock. • Customer 2 synchronized on his account and then accessed _numberOfTransactions before releasing the lock. But we dont have any particular guarantees about the order in which the operations executed. Suppose before either transaction that _numberOfTransactions was 13. The following sequence might have occurred: 1. The thread associated to customer 1 synchronized and loaded the value of _num - berOfTransactions 13 into its local cache. 2. The thread associated to customer 2 synchronized and loaded the value of _num - berOfTransactions 13 into its local cache. 3. The thread associated to customer 1 finished executing makeDeposit and, before releasing its lock, stored the value of the local copy of _numberOfTransactions 14 out to the main heap. 4. The thread associated to customer 2 finished executing makeDeposit and, before releasing its lock, stored the value of the local copy of _numberOfTransactions 14 out to the main heap. Another, equally possible, sequence: 1. The thread associated to customer 1 synchronized and loaded the value of _num - berOfTransactions 13 into its local cache. 2. The thread associated to customer 1 finished executing makeDeposit and, before releasing its lock, stored the value of the local copy of _numberOfTransactions 14 out to the main heap. 3. The thread associated to customer 2 synchronized and loaded the value of _num - berOfTransactions 14 into its local cache. 4. The thread associated to customer 2 finished executing makeDeposit and, before releasing its lock, stored the value of the local copy of _numberOfTransactions 15 out to the main heap. In one case, _numberOfTransactions increments correctly. In the other, it does not. The point is that if threads share state, they need to coordinate their caches. The only way for them to do this is to synchronize on the same lock. For example, we could replace the line: _numberOfTransactions++; with an invocation of the static method incrementNumberOfTransactions : public static synchronized void incrementNumberOfTransactions { _numberOfTransactions++; } public synchronized void makeDepositMoney amount throws RemoteException, NegativeAmountException { checkForNegativeAmountamount; _balance.addamount; incrementNumberOfTransactions ; return; } Declaring a static method as synchronized simply means that it synchronizes on the lock associated with the class object rather than on the lock associated with a particular instance. This is a very useful way of coordinating behavior between instances of the same class. Now, each thread synchronizes on the class object just before incrementing _num - berOfTransactions . This means that each request-handling thread is forced to reload _numberOfTransactions after obtaining the lock associated with the class object. Moreover, they write the value out to the main heap before relinquishing the lock associated with the class object. This extra layer of synchronization guarantees that _numberOfTransactions will be correctly incremented.

11.4.1.2 Acquiring the same lock more than once