Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 6
Making Example 4's singleton class thread-safe is easy—just synchronize the getInstance() method like this:
public synchronized static Singleton getInstance() {
if(singleton == null) {
simulateRandomActivity();
singleton = new Singleton();
}
logger.info("created singleton: " + singleton);
return singleton;
}
After we synchronize the getInstance() method, we can rerun Example 5's test case with the following results:
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:15)
compile:
[javac] Compiling 2 source files
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-1: created singleton: Singleton@ef577d
INFO Thread-2: created singleton: Singleton@ef577d
[java] .
[java] Time: 0.513
[java] OK (1 test)
This time, the test case works and our multithreading worries are over; however, the astute reader may realize that the getInstance() method only needs to be synchronized the first time it is called. Because synchronization is very expensive performance-wise
(synchronized methods can run up to 100 times slower than unsynchronized methods), perhaps we can introduce a performance
enhancement that only synchronizes the singleton assignment in getInstance().
In search of a performance enhancement, you might choose to rewrite the getInstance() method like this:
public static Singleton getInstance() {
if(singleton == null) {
synchronized(Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
Instead of synchronizing the entire method, the preceding code fragment only synchronizes the critical code. However, the
preceding code fragment is not thread-safe. Consider the following scenario: Thread 1 enters the synchronized block, and,
before it can assign the singleton member variable, the thread is preempted. Subsequently, another thread can enter the if block. The second thread will wait for the first thread to finish, but we will still wind up with two distinct singleton
instances. Is there a way to fix this problem? Read on.
Double-checked locking is a technique that, at first glance, appears to make lazy instantiation thread-safe. That technique is illustrated in the following code fragment:
public static Singleton getInstance() {
if(singleton == null) {
synchronized(Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
What happens if two threads simultaneously access getInstance()? Imagine Thread 1 enters the synchronized block and is preempted. Subsequently, a second thread enters the if block. When Thread 1 exits the synchronized block, Thread 2 makes a second check to see if the singleton instance is still null. Since Thread 1 set the singleton member variable, Thread 2's second check will fail, and a second singleton will not be created. Or so it seems.
Unfortunately, double-checked locking is not guaranteed to work because the compiler is free to assign a value to the singleton member variable before the singleton's constructor is called. If that happens, Thread 1 can be preempted after the singleton
reference has been assigned, but before the singleton is initialized, so Thread 2 can return a reference to an uninitialized
singleton instance.