单例模式的五种实现方式

来源:互联网 发布:先锋网络av功放接收机 编辑:程序博客网 时间:2024/05/13 12:00

1.最容易想到的方式
public class Singleton {
  
   private static final Singleton INSTSNCE = new Singleton();//单例
 
    private Singleton(){//构造函数私有
     
    }
    public static Singleton getInstance(){
     return INSTSNCE;
    } 
    public static void main(String[] args){
     Singleton s1 = Singleton.getInstance();
     Singleton s2 = Singleton.getInstance();
     System.out.println(s1 == s2);
    }
}
优点是:简单
缺点是:无论用没用到INSTSNCE,都会进行初始化。

2.延迟加载(lazy-load)
public class Singleton2 {
 
    private Singleton2(){
    }
    private static Singleton2 INSTANCE ;
   
    public synchronized static Singleton2 getInstance(){//同步方法,效率低
     if(INSTANCE == null){//lazy-loaded
      INSTANCE = new Singleton2();
     }
     return INSTANCE;
    }
    public static void main(String[] args){
     Singleton2 s1 = Singleton2.getInstance();
     Singleton2 s2 = Singleton2.getInstance();
     System.out.println(s1 == s2);
    }
}
优点:做到了延迟加载,也可以说是按需加载
缺点:方法同步,效率低

3.Double-Check-Lock
public class Singleton3 {
 private Singleton3(){
  
 }
 private volatile static Singleton3 instance;//volatile,保证可见性。各个线程的工作存储器和主存保持一致
 
 public static Singleton3 getInstance(){//double-check-lock
  if(instance == null){
   synchronized(Singleton3.class){
    if(instance == null){
     instance = new Singleton3();
    }
   }
  }
  return instance;
 }
    public static void main(String[] args){
     Singleton3 s1 = Singleton3.getInstance();
     Singleton3 s2 = Singleton3.getInstance();
     System.out.println(s1 == s2);
    }
}
注意这里的“volatile”关键字,我们知道jdk1.4以前,DCL是无法保证正常工作的,但是从jdk5开始,把instance定义成volatile就可以保证正常工作了。
为什么1.4以前,DCL不能正常工作,这主要跟jvm的内存模型有关。这就说来话长了,jvm把内存分为两类,主存储器和工作存储器,主存储器只有一份,但是,每一个线程都有自己的工作存储器,线程不能直接操纵主存储器,都是把主存里面的值拷贝到自己的工作存储器,然后操作自己的工作存储器,然后在适当的时候,写回到主存储器里面。volatile就保证了所有的线程的工作存储器和主存储器是一致的。
优点:延迟加载
缺点:只需要一次同步

4. Initialization on Demand Holder idiom
public class Singleton4 {

 private static class Singleton4Holder{//容器
  public static Singleton4 instance = new Singleton4();
 }
 private Singleton4(){
 }
 public static Singleton4 getInstance(){
  return Singleton4Holder.instance;
 }
 public static void main(String[] args) {
  Singleton4 s1 = Singleton4.getInstance();
  Singleton4 s2 = Singleton4.getInstance();
  System.out.println(s1 == s2);
 }
}
类载入到jvm的时候,会进行初始化,但是只会初始化static成员和static代码块,而不会初始化静态内部类的成员。因此,只有实际调用getInstance的时候,才会初始化静态内部类的静态成员。做到了延迟加载。同时,jvm的类加载机制保证了绝对不会出现同步问题,避免了双检查锁。
优点:延迟加载,而且不需要同步。

5.Enum
enum SingletonEnum {  
     INSTANCE {  
         public void someMethod() {  
          System.out.println("do something!");
         }  
     };  
     protected abstract void someMethod();  

public class Singleton5{
 public static void main(String args[]){
  SingletonEnum s1= SingletonEnum.INSTANCE;
  s1.someMethod();
  SingletonEnum s2= SingletonEnum.INSTANCE;
  System.out.println(s1 == s2);
 }
}
还有比这个更简洁明了的吗?

原创粉丝点击