Java Reference
In-Depth Information
Solution 85: Lazy Initialization
This program looks straightforward, if a bit strange. The static field
initialized
is initially set to
false
. Then the main thread creates a background thread whose
run
method sets
initialized
to
true
. The main thread starts the background thread and waits for it to complete by calling
join
.
Once the background thread has completed, there can be no doubt that
initialized
has been set to
TRue
. Then and only then does the main thread invoke
main
, which prints the value of
initialized
.
Surely the program must print
true
? If only it were so. If you ran the program, you found that it
prints nothing; it just hangs.
In order to understand the behavior of this program, we have to simulate its initialization in detail.
When a thread is about to access a member of a class, the thread checks to see if the class has been
initialized. Ignoring serious errors, there are four possible cases [JLS 12.4.2]:
1.
The class is not yet initialized.
2.
The class is being initialized by the current thread: a recursive request for initialization.
3.
The class is being initialized by some thread other than the current thread.
4.
The class is already initialized.
When the main thread invokes
Lazy.main
, it checks whether the class
Lazy
has been initialized. It
hasn't (case 1), so the thread records that initialization is now in progress and begins to initialize the
class. As per our previous analysis, the main thread now sets
initialized
to
false
, creates and
starts a background thread whose
run
method sets
initialized
to
TRue
, and waits for the
background thread to complete. Then the fun begins.
The background thread invokes its
run
method. Before the thread sets
Lazy.initialized
to
true
,
it too checks whether the class
Lazy
has been initialized. This time, the class is currently being
initialized by another thread (case 3). Under these circumstances, the current thread, which is the
background thread, waits on the
Class
object until initialization is complete. Unfortunately, the
thread that is doing the initialization, the main thread, is waiting for the background thread to
complete. Because the two threads are now waiting for each other, the program is deadlocked.
That's all there is to it, and what a pity it is.
There are two ways to fix the problem. By far the best way is not to start any background threads
during class initialization: Sometimes, two threads aren't better than one. More generally,
keep
class initialization as simple as possible.
A second way to fix the problem is to allow the main
thread to finish initializing the class before waiting for the background thread:
// Bad way to eliminate the deadlock. Complex and error prone.
public class Lazy {
Search WWH ::
Custom Search