Trail: Essential Java Classes
|
Lesson: Doing Two or More Tasks At Once: Threads
|
|
Locking an Object
The code segments within a program that access the same object from
separate, concurrent threads are called critical sections.
In the Java
language, a critical section can be a block or a method and are
identified with the synchronized keyword.
The Java platform then
associates a lock with every object that has synchronized code.
In the producer/consumer example, the put and
get methods of the CubbyHole (page 645) are
the critical sections. The Consumer should not access the
CubbyHole when the Producer is changing it,
and the Producer should not modify it when the
Consumer is getting the value. So put and
get in the CubbyHole class should be marked
with the synchronized keyword.
Here's a code skeleton for the CubbyHole class:
public class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
...
}
public synchronized void put(int value) {
...
}
}
Note that the method declarations for both put and
get contain the synchronized keyword.
Hence, the system
associates a unique lock with every instance of CubbyHole
(including the one shared by the Producer and the
Consumer). Whenever control enters a synchronized method,
the thread that called the method locks the object whose method has
been called. Other threads cannot call a synchronized method on the
same object until the object is unlocked.
So, when the Producer calls CubbyHole's
put method, it locks the CubbyHole, thereby
preventing the Consumer from calling the
CubbyHole's get method:
public synchronized void put(int value) {
// CubbyHole locked by the Producer
..
// CubbyHole unlocked by the Producer
}
When the put method returns, the Producer
unlocks the CubbyHole.
Similarly, when the Consumer calls
CubbyHole's get method, it locks the
CubbyHole, thereby preventing the Producer
from calling put:
public synchronized int get() {
// CubbyHole locked by the Consumer
...
// CubbyHole unlocked by the Consumer
}
The acquisition and release of a lock is done automatically and
atomically by the Java runtime system. This ensures that race
conditions cannot occur in the underlying implementation of the
threads, thus ensuring data integrity.
Synchronization isn't the whole story. The two threads must also be able to notify one another when they've done their job. Learn more about that after a brief foray into reentrant locks.
|