- As we talked about in the last lecture, threads can share
state.
- e.g. if every instance of a class has a thread, then they share
access to the class variables.
- This can be a problem!
- The classic example: An ATM (automatic teller machine).
- Suppose I and my partner are standing at two ATMs right next to
each other.
- I want to transfer money from our checking accout to my savings
account.
- My partner wants to take money out of my checking account.
- Suppose that these are the threads that get run:
//My Thread
transferAmount = ATM.getTypedNumber();
float checkingTotal = checking.getBalance();
float savingsTotal = savings.getBalance();
// would really have to catch if this makes the checkingTotal < 0!
checkingTotal -= transferAmount;
savingsTotal += transferAmount;
checking.setBalance(checkingTotal);
savings.setBalance(savingsTotal);
// My Partner's Thread
withdrawalAmount = ATM.getTypedNumber();
float checkingTotal = checking.getBalance();
// would really have to catch if this makes the checkingTotal < 0!
checkingTotal -= withdrawalAmount;
ATM.squirtMoneyOut(withdrawelAmount);
checking.setBalance(checkingTotal);
- What makes ATM examples interesting is the squirtMoneyOut command.
- Once the customer has the money, nothing the program can do
will get it back!!
- Now suppose that both of our threads read the original checking
balance from before either of us has changed it.
- We get free money!
- The final checking balance will only reflect either
the withdrawal or the
transfer, not both!
- But we have more money in savings and my
partner has cash too.
- This may sound cool, but actually it's not.
- If we'd been depositing money, we could have lost money in the
same way.
- No nation / economy can do very well if their banks don't work
better than this!
- The solution is called locking.
- If a thread is going to do multiple things to some memory /
state / a variable (esp. read it then change it!) then it locks that
variable.
- A lock prevents other threads from accessing the value.
- If they try to, they block
- basically they wait until they can get access to it.
- Locking in Java is done via synchronization.
- There are two ways to use synchrony:
- synchronized methods, and
- synchronized statements.
- Only one thread can call
synchornized code on an object at a time. One way to do this is
by declaring synchronized methods:
public synchronized float debitAccount (Account a, float amount) {
if (a.getBalance - amount < 0) {
throw new BalanceLTZeroException ("some clever message");
}
a.setBalance(a.getBalance() - amount);
return (a.getBalance());
}
- Notice that I haven't only solved the problem by creating a
synchronized method.
- I also had to create essentially a new way of accessing the
account
balance. All other accessors should either be synchronized, made private or
got rid of!
- This is because we haven't really locked the attribute,
we've locked a method.
- If you want to lock just individual elements of data rather
than code, you need to use a database (more on this next year!)
- If your method is long you may not want to declare the whole
thing synchronized.
- Don't want to cut down on parallelism.
- Want to let other threads have a go.
- The synchroinze statement is
another way to create synchonized code.
float checkingTotal;
synchronize (checking) {
checkingTotal = checking.balance();
checkingTotal -= transferAmount;
checking.balance(transferAmount);
}
System.Out.printline("You have "+ checkingTotal +" in your checking account");
- Notice synchronize in this context
takes an argument (an object)
- Every object has an implicit
lock, which is what locks when you call a synchronized method.
- If you use that object in the
synchronize statement, it will also lock any other access to that
object with synchronized code using either way of synchronizing.
- You can also create objects just
to use their locks if you want to have finer-grained locking.
- Notice: you can still get at the
object if you use unsynchronized
code!
- So in other words, this
only blocks access from synchonized methods of the object, or of other
(or the same!) sychronized program blocks.
- Java locks code, not data.
- You have to synchronize a lot of things!
- Most people wind up using databases to
address this (see next year.)
- Again, you don't want to do this very often or for very long
bits of code,
because that will reduce the benefit of having threads in the first
place.