以下是线程不安全的延迟初始化
public class UnsafeLazyInitialization {
private static Resource resource;
public static Resource getInstance() {
if (resource == null)
resource = new Resource(); // unsafe publication
return resource;
}
}
因为线程A写入resource的操作与线程B读取resource的操作之间不存在Happens-Before的关系,在发布对象时存在数据竞态问题,因此B并不一定能看到Resource的正确状态。
With the exception of immutable objects, it is not safe to use an object that has been initialized by another thread unless the publication happens before the consuming thread uses it.
- 线程安全的延迟初始化
通过加锁的方式保证happens-before
public class SafeLazyInitialization {
private static Resource resource;
public synchronized static Resource getInstance() {
if (resource == null)
resource = new Resource();
return resource;
}
}
- 提前初始化
Static initializers are run by the JVM at class initialization time, after class loading but before the class is used by any thread. Because the JVM acquires a lock during initialization and this lock is acquired by each thread at least once to ensure that the class has been loaded, memory writes made during static initialization are automatically visible to all threads. Thus statically initialized objects require no explicit synchronization either during construction or when being referenced.
public class EagerInitialization {
private static Resource resource = new Resource();
public static Resource getResource() {
return resource;
}
}
- 延迟初始化站位模式(非常推荐)
The JVM defers initializing the ResourceHolder class until it is actually used , and because the Resource is initialized with a static initializer, no additional synchronization is needed. The first call to getResource by any thread causes ResourceHolder to be loaded and initialized, at which time the initialization of the Resource happens through the static initializer.
public class ResourceFactory {
private static class ResourceHolder {
public static Resource resource = new Resource();
}
public static Resource getResource() {
return ResourceHolder.resource;
}
}
- 双重检查加锁(不推荐)
这里通过volatile来保证线程安全性。由于无竞争同步执行很快,所以DCL模式不是一个高效的优化措施。
public class DoubleCheckedLocking {
private volatile static Resource resource;
public static Resource getInstance() {
if (resource == null) {
synchronized (DoubleCheckedLocking.class) {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}
- 不可变对象的初始化安全性
For objects with final fields, initialization safety prohibits reordering any part of construction with the initial load of a reference to that object. All writes to final fields made by the constructor, as well as to any variables reachable through those fields, become “frozen” when the constructor completes, and any thread that obtains a reference to that object is guaranteed to see a value that is at least as up to date as the frozen value. Writes that initialize variables reachable through final fields are not reordered with operations following the post-construction freeze
import java.util.HashMap;
public class SafeStates {
private final Map<String, String> states;
public SafeStates() {
states = new HashMap<String, String>();
states.put("alaska", "AK");
states.put("alabama", "AL");
...
states.put("wyoming", "WY");
}
public String getAbbreviation(String s) {
return states.get(s);
}
}
Initialization safety makes visibility guarantees only for the values that are reachable through final fields as of the time the constructor finishes. For values reachable through nonfinal fields, or values that may change after construction, you must use synchronization to ensure visibility.