单例模式,非常常用,比如在JVM中方法去的常量池单例设计(想到了String.intern()方法),可以保证对象只有一个实例存在,本节就探讨怎么高效实现单例模式。
延迟初始化,是延迟到需要域的值时才将它初始化的行为,如果永远不需要这个值,这个域就永远不会被初始化。这种技术只在类的实例部分被访问,并且初始化这个域的开销很高的时候,考虑延迟初始化。延迟初始化技术的典型应用就是单例模式。
1. 正常的初始化方法
public class Singleton {
private static Singleton instance=new Singleton();//静态的域
private Singleton(){} //私有化构造函数,防止创建实例
public static Singleton getInstance(){ //静态的方法
return instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
2.非线程安全的延迟初始化方法
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null)
instance=new Singleton();
return instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
3. 加synchronized的线程同步,因为是对对象上锁(所以对象内的所有方法都不能用?才导致性能下降?)
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance==null)
instance=new Singleton();
return instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
4.双重检查加synchronized的线程同步方法(实例域)
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (instance) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
但是这仍然存在潜在危险,因为Java指令中创建对象和赋值操作是分开进行的,而且JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例,继续优化。
5.双重检查对volatile域加synchronized的线程同步方法(实例域的最优解)
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (instance) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
为了避免JIT编译器对代码的指令重排序优化,使用了volatile关键字生命域,从而禁止变量不会在多个线程中缓存副本,变量可以看成是直接从主内存中读取,相当于实现了一个轻量级的锁。
6.内部静态类的实现方法(静态域的最优解)
public class Singleton {
private Singleton(){}
private static class SingletonFactory{
private static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonFactory.instance;
}
public static void main(String[] args) {
Singleton s=getInstance();
}
}
这是因为当JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。