单线程中的线程安全
我知道Java中的双重锁定被破坏了,那么在Java中使单线程安全的最好方法是什么? 我想到的第一件事是:
class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
这是否工作? 如果是的话,这是否是最好的方法(我认为这取决于具体情况,所以说明一种特定技术何时是最好的,将会很有用)
Josh Bloch建议使用单元素enum
类型来实现单例(请参阅Effective Java第2版,第3项:使用私有构造函数或枚举类型强制实现singleton属性)。
有人认为这是一种黑客行为,因为它没有明确表达意图,但它确实有效。
以下示例直接来自本书。
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
这是他的结论性论点:
这种方法更简洁,提供了免费的序列化机制,并提供了针对多个实例的铁证保证,即使面对复杂的序列化或反射攻击。 虽然这种方法尚未被广泛采用,但单元枚举类型是实现单例的最好方法 。
关于enum
常量单例保证
JLS 8.9。 枚举
一个枚举类型除了由枚举常量定义的枚举类型之外没有其他实例。 尝试显式实例化枚举类型是一个编译时错误(第15.9.1节)。
Enum
的final clone
方法确保枚举常量永远不会被克隆,并且序列化机制的特殊处理可确保重复实例从不会因反序列化而被创建。 禁止枚举类型的反射实例化。 总而言之,这四件事确保枚举类型的实例不会超出枚举常量定义的实例。
在延迟初始化
以下片段:
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);
}
}
生成以下输出:
La-dee-daaa...
I'M STILL ALIVE!!!
THE_ONE
正如你所看到的, THE_ONE
常量在第一次被访问之前不会通过构造函数实例化。
我认为你的实现没有问题(除了单身监视器的锁可能被其他方法使用,或者由于其他原因,并且因此不必要地阻止其他线程获得实例)。 这可以通过引入一个额外的Object lock
来锁定来避免。
这篇维基百科文章提出另一种方法:
public class Something {
private Something() {
}
private static class LazyHolder {
private static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
从文章:
这个实现是一个在所有Java版本中都有效的并行实现。
...
该实现依赖于Java虚拟机(JVM)内执行良好的指定初始化阶段; 有关详细信息,请参阅Java语言规范(JLS)的第12.4节。
我的首选是做:
class Singleton {
private static final INSTANCE = new Singleton();
private Singleton(){}
public Singleton instance(){
return INSTANCE;
}
}
你很少需要懒惰的初始化。 你应该总是从急切的初始化开始,如果你看到问题,只能改为懒惰的初始化。 除非你已经测量并确定Singleton实例化是性能问题的罪魁祸首,只需使用预先初始化即可。 它更简单,更高效。
你可以肯定地使用枚举,但我个人并不打扰,因为正常实例化的好处是安全性(针对反射攻击),并且大多数时候我的代码易受这种攻击的影响:p
链接地址: http://www.djcxy.com/p/16335.html