Rob*_*ert 13 java singleton multithreading
我知道Java中的双重锁定已被破坏,那么在Java中使单例线程安全的最佳方法是什么?我想到的第一件事是:
class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
这有用吗?如果是这样,它是最好的方式(我想这取决于具体情况,所以说明特定技术最好的时候会有用)
pol*_*nts 25
Josh Bloch建议使用单元素enum类型来实现单例(请参阅Effective Java第2版,第3项:使用私有构造函数或枚举类型强制执行单例属性).
有些人认为这是一个黑客,因为它没有明确表达意图,但确实有效.
以下示例直接来自本书.
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Run Code Online (Sandbox Code Playgroud)
以下是他的结论:
这种方法更简洁,免费提供序列化机制,并提供针对多个实例的铁定保证,即使面对复杂的序列化或反射攻击.虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方法.
enum恒定单身保证枚举类型没有除其枚举常量定义的实例之外的实例.尝试显式实例化枚举类型(第15.9.1节)是编译时错误.
该
final clone方法Enum确保永远不会克隆枚举常量,并且序列化机制的特殊处理确保不会因反序列化而创建重复实例.禁止对枚举类型进行反射实例化.总之,这四件事确保除了枚举常量定义的实例之外不存在枚举类型的实例.
以下片段:
public class LazyElvis {
enum Elvis {
THE_ONE;
Elvis() {
System.out.println("I'M STILL ALIVE!!!");
}
}
public static void main(String[] args) {
System.out.println("La-dee-daaa...");
System.out.println(Elvis.THE_ONE);
}
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
La-dee-daaa...
I'M STILL ALIVE!!!
THE_ONE
Run Code Online (Sandbox Code Playgroud)
如您所见,THE_ONE在第一次访问常量时,常量不会通过构造函数进行实例化.
我发现你的实现没有问题(除了由于其他原因而其他方法可能会使用singleton-monitor的锁定这一事实,因此,不必要地阻止其他一些线程获取实例).这可以通过引入额外Object lock的锁定来避免.
这篇Wikipedia文章提出了另一种方法:
public class Something {
private Something() {
}
private static class LazyHolder {
private static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
Run Code Online (Sandbox Code Playgroud)
来自文章:
此实现是一个性能良好且并发的实现,在所有Java版本中都有效.
...
实现依赖于Java虚拟机(JVM)中明确指定的初始化执行阶段; 有关详细信息,请参阅Java语言规范(JLS)的第12.4节.
我的偏好是:
class Singleton {
private static final INSTANCE = new Singleton();
private Singleton(){}
public Singleton instance(){
return INSTANCE;
}
}
Run Code Online (Sandbox Code Playgroud)
您很少需要延迟初始化.您应始终从急切初始化开始,如果发现问题,只能更改为延迟初始化.除非你已经测量并精确定位了Singleton实例化作为性能问题的罪魁祸首,否则只需使用急切的初始化.它更简单,更高效.
您可以肯定使用枚举,但我个人并不打扰,因为普通急切实例化的好处是安全性(针对反射攻击),并且大多数时候我的代码很容易受到此类攻击:p