从Head First设计模式书中,具有双重检查锁定的单例模式已实现如下:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么volatile被使用.volatile使用不会 破坏使用双重检查锁定的目的,即性能?
java singleton design-patterns locking double-checked-locking
下面的例子来自Brian Goetz的书"Java Concurrency in Practice",第3章,第3.5.1节.这是不正确发布对象的示例
class someClass {
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
}
public class Holder {
private int n;
public Holder(int n) { this.n = n; }
public void assertSanity() {
if (n!=n)
throw new AssertionError("This statement is false");
}
}
Run Code Online (Sandbox Code Playgroud)
它表示Holder可能出现在另一个处于不一致状态的线程中,而另一个线程可能会观察到部分构造的对象.怎么会发生这种情况?你能用上面的例子给出一个场景吗?
此外,它继续说有一种情况,当一个线程第一次读取一个字段时可能会看到一个陈旧的值,然后下次再看到一个更新的值,这就是assertSanity可以抛出断言错误的原因.如何抛出assertionError?
从进一步阅读,解决这个问题的一种方法是通过使变量'n'最终使Holder不可变.现在,让我们假设持有人不是无法忍受的,而是有效的不可改变的.为了安全地发布这个对象,我们是否必须使holder初始化为静态并将其声明为volatile(静态初始化和volatile或者只是volatile)?就像是
public class someClass {
public static volatile Holder holder = new Holder(42);
}
Run Code Online (Sandbox Code Playgroud)
感谢您的帮助.
嗨,下面是Effective Java 2nd Edition的片段.在这里,作者声称下面的代码比你不使用结果变量快25%.根据书中的"这个变量的作用是确保该字段在已经初始化的常见情况下只读取一次." .我无法理解为什么这个代码在初始化之后会比较快,如果我们不使用Local变量结果.在任何一种情况下,无论是否使用局部变量结果,初始化后只有一个易失性读取.
// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
Run Code Online (Sandbox Code Playgroud) 值得注意的是,在新内存模型下,像双重检查锁定这样的破坏技术仍然被破坏,并且“修复”双重检查锁定并不是新内存模型努力的目标之一。(然而, volatile 的新语义允许双重检查锁定的常用替代方案之一正常工作,尽管该技术仍然不鼓励使用。)
相关讨论:
我可以理解为什么它在没有volatile修复的情况下就坏了。但我不知道为什么即使在修复之后仍然不鼓励它。我注意到在其中一次讨论中存在一些分歧。但如果它真的如参考所建议的那样?这个固定版本仍然不鼓励的原因是什么?
采取以下java代码:
public class SomeClass {
private boolean initialized = false;
private final List<String> someList;
public SomeClass() {
someList = new ConcurrentLinkedQueue<String>();
}
public void doSomeProcessing() {
// do some stuff...
// check if the list has been initialized
if (!initialized) {
synchronized(this) {
if (!initialized) {
// invoke a webservice that takes a lot of time
final List<String> wsResult = invokeWebService();
someList.addAll(wsResult);
initialized = true;
}
}
}
// list is initialized
for (final String s : someList) {
// …Run Code Online (Sandbox Code Playgroud) java concurrency multithreading synchronization memory-model