我遇到过一些代码,其中开发人员不断检查单例是否为空两次,嵌套的if -类似于下面的代码:
private static processManager singleton = null;
…
public synchronized static processManager getInsatnce() throws Exception {
if(singleton == null) {
if(singleton == null){
singleton = new processManager();
}
}
return singleton
}
我看不出这可能是什么原因,但是代码中有很多实例,所以认为可能有原因?
您的代码没有正确地演示这种情况。这源于反复检查的习惯用法,在这里它是有意义的:
// 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;
}
看这里
请注意,这种习惯用法仅适用于实例字段。在您的问题中,您有一个static
字段,在这种情况下,一个更简单的习惯用法是主要选择:惰性初始化持有人类习惯用法:
// Lazy initialization holder class idiom for static fields
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field; }
这是实现双重检查锁定习惯用法的失败尝试。第一个null
检查是查看实例是否已经创建,如果是not null
,则返回已经创建的实例。
但是条件检查是检查-处理情况,并且不是线程安全的,因此有可能两个或多个线程将看到值为null
并创建两个单例和双例或ManyTon实例。
所以我们使用synchronized
,所以只有一个线程进入该块,只有一个实例被创建。
我想你指的是双重检查锁定。此模式允许您在不需要同步时避免同步。
你的代码应该是private static volatile ProcessManager singleton = null;
public static ProcessManager getInstance() throws Exception {
if (singleton == null) {
synchronized (MyClass.class) {
if (singleton == null) {
singleton = new ProcessManager();
}
}
}
return singleton;
}
所以你看,我们只同步当我们已经检查了单例不为空,然后我们重新检查,以防有人已经开始构建它。注意,要使其工作,单例必须是 volatile
。这里有一篇文章,解释了如果您忘记volatile
,会出现的微妙问题。
在你的例子中,方法是同步的,你是对的。检查两次是没有意义的。
除非单例是在getter中创建实例的属性,否则这没有任何意义,但即使这样,这也没有意义,否则其余的代码将无法访问。
这种技术叫做双重检查。
但是,您粘贴的代码是不正确的。你是对的,以这种方式检查空值是没有意义的。我想说双重检查的正确实现如下:private static volatile ProcessManager instance = null;
public static ProcessManager getInstance() {
if (instance == null) {
synchronized(ProcessManager.class) {
if (instance == null) {
instance = new ProcessManager();
}
}
}
return instance;
}
注意,第二个空检查是在ProcessManager.class对象上同步的。这是必要的,因为getInstance()方法是静态的。