(lively music)
- [Bethan] When using multithreaded applications,
problems can sometimes occur
when more than one thread is inside the same method.
In this example, I have a program
that simulates withdrawing money from a bank account.
So first, I have a class called BankAccount.
This has a field called balance,
which represents the amount of money in the account.
Then there is a second field called overdraft,
which represents the overdraft limit.
The constructor takes a number
for the overdraft as an argument.
There are also methods to top up the account,
debit the account,
get the balance and get the overdraft.
Secondly, I have a class called ATM.
This has a method called withdraw,
which takes two arguments.
The first is the account the money
is being withdrawn from
and the second is the amount to be withdrawn.
It then checks if the amount being withdrawn
would take the money
in the account over the overdraft limit.
If it does, then it prints out a warning message
and doesn't remove any money from the account.
Otherwise, it debits the account
by the amount specified.
Finally, I have a class called users.
This class has an account with an overdraft limit of $50.
In the main method of the class,
I firstly top up the account with $100.
They have $100 in their account
and an overdraft of $50.
So they can spend up to $150 without being overdrawn.
In this example, I am assuming
that one account can have two cards associated with it.
So there could be a scenario
where two people try to withdraw money
from the account at exactly the same time.
To represent this situation,
I have two threads running at the same time.
In the first thread, t1,
someone goes to an ATM
and tries to withdraw $100.
And the second person also tries
to withdraw $100 from the account.
I start running both threads at the same time
to see what happens.
When I run the application,
the output shows that the current balance is -100
but the overdraft limit is $50.
So something has gone wrong.
If I look again
at the withdraw method in the ATM class,
I can see what might have happened.
The first thread has entered the withdraw method
and called getBalance.
This returns $100
because that is the amount in the account to start with.
It then moves on to check if withdrawing the money
from the account will make it overdrawn.
At this point, the second thread
could have entered the method
and called the getBalance method.
Because the first thread hasn't withdrawn any money,
it also returns $100.
So both threads are able to withdraw $100 each,
taking the account balance below the overdraft.
There is no knowing which order the threads
will execute the lines in the method in.
So sometimes it may work perfectly fine
but sometimes, like this time, it doesn't.
This may seem like a complex problem to solve
but it's actually very simple.
All I have to do is add the synchronized keyword
to the signature of the withdraw method
right before the return type.
This means that now only one thread
can enter this method at a time.
So the first thread will enter the method,
get the balance which will be $100,
then withdraw $100.
So the balance is now $0.
A second thread can only enter the method
when the first thread has finished.
So when it calls the getBalance method,
it will be $0.
Withdrawing $100 would take it over the $50 overdraft limit
so the transaction would be denied.
If I rerun the program,
I can now see that it works as intended.
It is now impossible
for both threads to enter the withdraw method
at the same time.
So synchronized methods are a simple
and effective way to stop threads
from interfering with object data at the same time.
(upbeat music)