Java Reference
In-Depth Information
When the
updateBalance()
method is invoked, the CPU has to execute six instructions to add
10
to and subtract
10
from the
balance
variable. When the
balance
update thread is in the middle of executing any of the first three
instructions, the balance monitor thread will read the
balance
value as 100. When the balance update thread has
finished executing the third instruction, the balance monitor thread will read its value as
110
. The value
110
for the
balance
variable will be restored to 100 only when the balance update thread executes the sixth instruction. Note
that if the balance monitor thread reads the value of the
balance
variable any time after the execution of the third
instruction and before the execution of the sixth instruction by the balance update thread, it will read a value that is
not the same as the value that existed at the start of the
updateBalance()
method execution. Table
6-1
shows how the
value of the
balance
variable will be modified and read by the two threads.
Table 6-1.
Instruction Executions for Multiple Threads
Statement (Suppose Balance
Value is 100 to Start With)
Instructions Being Executed by
the Balance Update Thread
The Value of Balance Read by
the Balance Monitor Thread
balance = balance + 10;
register-1 = balance;
100
register-1 = register-1 + 10;
100
balance = register-1;
Before execution: 100
After execution: 110
balance = balance - 10;
register-2 = balance;
110
register-2 = register-2 - 10;
110
balance = register-2;
Before execution: 110
After execution: 100
In your program, the monitor thread was able to read the value of the
balance
variable as 110 because you
allowed two threads to modify and read the value of the
balance
variable concurrently. If you allowed only one thread
at a time to work with (modify or read) the
balance
variable, the balance monitor thread would never read the value
of the
balance
variable other than
100
.
The situation where multiple threads manipulate and access a shared data concurrently and the outcome
depends on the order in which the execution of threads take place is known as a
race condition
. A race condition in
a program may lead to unpredictable results. Listing 6-4 is an example of a race condition where the program output
depends on the sequence of execution of the two threads.
To avoid a race condition in a program, you need to make sure that only one of the racing threads works with the
shared data at a time. To solve this problem, you need to synchronize the access to the two methods
updateBalance()
and
monitorBalance()
of the
BalanceUpdate
class. That is, only one thread should access one of these two methods
at a time. In other words, if one thread is executing the
updateBalance()
method, another thread that wants to
execute the
monitorBalance()
method must wait until the thread executing the
updateBalance()
method is
finished. Similarly, if one thread is executing the
monitorBalance()
method, another thread that wants to execute
the
updateBalance()
method must wait until the thread executing the
monitorBalance()
method is finished. This
will ensure that when a thread is in the process of updating the
balance
variable, no other threads will read the
inconsistent value of the
balance
variable and vice versa.
This kind of problem that needs synchronizing the access of multiple threads to a section of code in a Java
program can be solved using the
synchronized
keyword. To understand the use of the
synchronized
keyword, I need
to discuss the Java Memory Model in brief, and the lock and wait sets of an object.