设计模式: 单例模式

Posted by ChenJY on January 12, 2017 | Viewed times
本站图床基于新浪微博,图片加载异常请强制刷新或直接访问语雀空间查阅文章备份

概念:

Java单例模式是一种常见的设计模式,单例模式的写法有好几种,主要的包括三种:懒汉式饿汉式登记式枚举式。 单例模式有以下特点:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

优点:

  • 1、在内存中只有一个对象,节省内存空间。
  • 2、避免频繁的创建销毁对象,可以提高性能。
  • 3、避免对共享资源的多重占用。
  • 4、可以全局访问。

懒汉式单例

//懒汉式单例类.在第一次调用的时候实例化自己   
public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //静态工厂方法   
    public static Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
    }  
}

Singleton 通过将构造方法限定为 private 避免了类在外部被实例化,在同一个虚拟机范围内,Singleton 的唯一实例只能通过 getInstance() 方法访问。

但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个 Singleton 实例,如果现在存在着 线程AB,代码执行情况是这个样子的,线程A 执行到了 If(singleton == null)线程B 执行到了 Singleton = new Singleton()

线程B虽然实例化了一个 Singleton,但是对于线程A来说判断 Singleton 还是未初始化的,所以 线程A 还会对 Singleton 进行初始化。

第一次尝试

我们可以通过添加 Synchronized 关键词来解决同步问题:

public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //静态工厂方法   
    public static Singleton getInstance() {  
         if (single == null) {    
             synchronized(Singleton.class){
                single = new Singleton();  
             }
         }    
        return single;  
    }  
}

通过 Synchronized 关键词我们貌似成功的解决了多线程的问题,但是仔细想想还是不正确,因为AB两个线程可能同时等待进入 synchronized(Singleton.class) 这句话,而之后又不再判断一次 single 是否为 null,那么还是会出现多个实例(因为AB均可以先后进入这个代码块)。因此,我们更好的解决办法是双重判断:

第二次尝试

public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //静态工厂方法   
    public static Singleton getInstance() {  
         if (single == null) {    
             synchronized(Singleton.class){
                 if(single == null){
                    single = new Singleton();  
                 }
             }
         }    
        return single;  
    }  
}

当然,我们也可以将 Synchronized 关键词放在 public 方法上,同样可以起到效果,只是锁定这个方法会带来较大的效率损失。

改进方法

//静态内部类,既实现了线程安全,又避免了同步带来的性能影响
public class Singleton {    
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton();    
    }    
    private Singleton (){}    
    public static final Singleton getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
}  
//枚举单例实现
public class Singleton {
    private static enum Singleton {
        INSTANCE;
        private Singleton single;
        private Singleton(){
            single = new Singleton();
        }
        private Singleton getInstance(){
            return single;
        }
    }
}

饿汉式单例

//饿汉式单例先自己new一个实例,在你需要的时候直接返给你
public class Singleton{
       private static final Singleton singleton = new Singleton();
       private Singleton(){}
       public static Singleton getInstance(){
          return singleton;
       }
}
  • 优点是:写起来比较简单,而且不存在多线程同步问题,避免了 Synchronized 所造成的性能问题;
  • 缺点是:当类 SingletonTest 被加载的时候,会初始化 staticinstance,静态变量被创建并分配内存空间,从这以后,这个staticinstance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。

许可协议


Comment