我想知道枚举中的单身人物及其表现。
当我们有多线程环境时,我们必须在创建实例时同步时刻。
简单地说,我们可以使用synchronized mod,用于创建实例的名为getInstance()的函数 有些想法:
public synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
它的实施很懒,对我有好处。但是同步方法很慢。 我们可以使用双锁来加快速度。
枚举怎么样? 当我们将单例实现为枚举时,将首次使用单例实例。下次使用,返回当前实例。
它是如何工作的? 当我们想要获取现有实例时,隐式同步方法很慢?还是实施了双锁?
答案 0 :(得分:3)
Enum是线程安全的,并且the recommended way也可以实现Singleton。
那就是说,枚举并不是懒惰的。如果你想要一个延迟加载的单例,你可以使用Holder pattern(也是线程安全的):
class LazySingleton {
private LazySingleton() {}
private static class SingletonHelper{
private static final LazySingleton INSTANCE = new LazySingleton();
}
public static LazySingleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
答案 1 :(得分:1)
enum
没有延迟初始化。它只会在加载类时创建实例。
当我们将单例实现为枚举时,将首次创建单例实例。
只有通过“第一次使用”,你才真正意味着“当班级被加载”时才会出现这种情况。
换句话说,使用您的示例,枚举将等同于:
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
答案 2 :(得分:1)
enum
常量的初始化发生在类初始化程序中,类似于你编写的
static final Singleton instance = new Singleton();
或
static final Singleton instance;
static {
instance = new Singleton();
}
安全来自the JVM perform class initialization under a JVM specific lock:
这一事实由于Java编程语言是多线程的,因此初始化类或接口需要仔细同步,因为其他一些线程可能正在尝试同时初始化同一个类或接口。 ... Java虚拟机的实现负责通过使用以下过程来处理同步和递归初始化。
...
对于每个类或接口
C
,都有一个唯一的初始化锁LC
。从C
到LC
的映射由Java虚拟机实现决定。...
我从规范中遗漏了很多技术细节,对于Java程序员来说,最重要的一点是每个符合JVM的实现都有一个安全机制。该部分的结尾有注释:
实现可以通过在步骤1中删除锁获取(并在步骤4/5中释放)来优化此过程,此时它可以确定类的初始化已经完成,前提是,就记忆模型,所有发生的 - 在获得锁定时存在的排序,在执行优化时仍然存在。
当然,这是单例模式实现的一个重点,以后访问static final
字段不需要获取锁。由于所有类都从未初始化状态到初始化状态只有一次,并且它影响每个操作(包括实现单例模式的所有其他可能性),因此您可以期望每个JVM都执行此基本优化。即使特定的JVM不这样做,static final
字段也是该虚拟机上最快的懒惰单例实现...