单例模式的几种实现方式

单例模式是现如今非常普遍的模式之一。它是一种对象创建模式,用于生产一个对象的具体实例,它可以确保一个系统中一个类只产生一个实例。在java中,这样的行为带来两种好处:

1):对于频繁创建的对象,可以省略对象创建所花费的时间,对于一些重量级对象而言,是非常可观的系统开销。2):由于new的操作减少,因而对系统内存的使用频率也会降低,这将减轻GC的压力,缩短GC的停顿时间。

因此对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效改善系统性能。下面介绍几种单例模式的写法:

1.饿汉模式
public Singleton {

    private Singleton {
        System.out.println("Singleton is create")//创建单例的过程可能会比较慢
    }

    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

}
这是最为普遍的一种方法,简单易懂。注意代码中重点标注的部分,首先单例必须要有一个private访问级别的构造函数。只有这样才能保证不会在系统中其他代码中实例化,这点是相当重要的;其次instance成员变量和getInstance()方法必须是static的。使用者通过Singleton.getInstance()就可以获得相关单例(类加载时创建instance对象创建于静态内存区内并且只有一个拷贝。在这里注意静态方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。而且因为实例成员与特定的对象关联,只能访问所属类的静态成员变量和成员方法。)。

不过它的不足也显而易见,就是无法对instance实例做延时加载。假如单例的创建过程很慢,而由于instance成员变量时static定义的,因此JVM加载单例类时,单例对象就会在静态内存区里建立。如果此时此单例在系统中还扮演其他角色(就是用到其他静态方法或变量),那么任何使用这个单例类的时候都会初始化这个单例变量instance,而不管是否会被用到(即任何Singleton.otherStaticMethod()时候,都会执行其构造方法输出"Singleton is create"而造成不必要的时间和内存的开销)。为了解决这个问题我们需要引入一个延迟加载机制,就是下面的懒汉模式。

2.懒汉模式
public LazySingleton {

    private LazySingleton {
        System.out.println("LazySingleton is create")//创建单例的过程可能会比较慢
    }

    private static LazySingleton instance = null;

    public static LazySingleton getInstanceNotSafe() {//线程不安全
        if(instance = null) {
            instance = new LazySingleton;
        }
        return instance;
    }

  
  public static synchronized LazySingleton getInstanceSafe() {//线程安全
        if(instance = null) {
            instance = new LazySingleton;
        }
        return instance;
    }

}
这里首先对于静态变量instance初始值赋予null,确保类加载时没有额外的负载。其次在getInstance工厂方法中,判断当前单例是否存在,若存在则返回不存在时再建立单例。注意上述中getInstanceNodSafe()方法不是同步的,故在多线程的环境下线程1正新建单例时,完成赋值操作,这时线程2判断instance为null,故线程2也将新建单例的程序,而导致多个实例被创建而导致非单例。所以在多线程的环境中使用引入同步关键字的getInstanceSafe()的方法,但是因为同步需要等待它的时耗远远大于饿汉模式。

以下测试代码说明了这个问题:
    public void run() {
        for(int i = 0 ;i < 100000 ;i++) {
            Singleton.getInstance();
            //LazySingleton.getInstanceSafe();
        }
        System.out.println("spent:" + (System.currentTimeMillis() - begintime));
    }
开启五个线程同时完成以上代码,饿汉模式的单例耗时0ms,而使用LazySingleton却相对耗时约390ms。性能上至少相差两个数量级。为了延迟加载引入了同步关键字后降低了性能,为使这个同步方法更为有效一个双重检查锁定的模式应运而生了。

3.双重检查锁定模式
public Singleton {

    private Singleton {
        System.out.println("Singleton is create")//创建单例的过程可能会比较慢
    }

    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
          if(instance == null) {
                   instance = new Singleton();
          }
            }
        }
     return instance;
    }

}     
此模式先判断instance是否为null,之后才进入同步语句。当第一个线程1与第二个线程2并发进入第一个if语句后,一个线程进入synchronized块来初始化instance而另一个线程则被阻断。当第一个线程退出synchronized块时,等待着的线程2进入并判断instance是否已创建再返回instance。至此对instance进行两次检查。这也是“双重检查锁定”名称的由来。与懒汉模式相比不用每次调用getinstance()都付出同步的代价,只有第一次创建才会同步,创建之后就没用了。

4.静态内部类模式
public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton () {
        System.out.println("StaticInnerClassSingleton is create");
    }   

    private static class SingletonHolder {
      private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton();//只会加载一次得到单个实例
    }

    public static StaticInnerClassSingleton getInstance() {
      return SingletonHolder.instance;
    }
}
在这个实现中,单例模式内部类来维护单例的实例。当StaticInnerClassSingleton加载时,其内部类不会被初始化,故StaticInnerClassSingleton类被载入JVM时,不会初始化单例类,而当getInstance()方法被调用时,才会加载SingletonHolder,从而初始化。同时,由于实例建立是在内部类加载时完成,故天生对多线程友好。getInstance()方法也不需要使用同步关键字。因此与饿汉模式相比等待延迟加载instance,所以不用担心只想调用其他静态方法时会创建一个单例。

5.总结与漏洞修复

通常情况下,用以上方式实现单例已经可以确保在系统中只存在唯一的实例,个人比较推荐用第三或第四种方法。但仍然有例外的情况,可能导致系统生成多个实例,比如在代码中,通过反射机制,强行调用单例类的私有构造函数生成多个单例,但是我们在这里先不讨论这种极端方式。但是仍有些合法的方法可能导致多个单例的产生,如:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对于第一个问题修复方法:
private static Class getClass(String classname) throws ClassNotFoundException {
          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();   

      if(classLoader == null){
        classLoader = Singleton.class.getClassLoader();
      }
      return (classLoader.loadClass(classname));
   }
}
对于第二个问题的修复方法:
public class Singleton implements java.io.Serializable {
   public static Singleton instance = new Singleton();   

   protected Singleton() {
      System.out.println("Singleton is create");
   }
   private Object readResolve() {  //这里修复,阻止生成新的实例总是返回当前对象
            return instance;
   }
} 


 
时间: 2024-10-06 20:40:58

单例模式的几种实现方式的相关文章

JAVA中单例模式的几种实现方式

1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线 程安全就不会使用这种方式.但是如果不需要保证线程安全,则这种方式还是不错的,因为所需要的开销比较小.下面是具体的实现代码: 转http://www.cnblogs.com/CodeGuy/p/3580486.html public Class Singleton { private static Singleton instance

单例模式的三种实现方式

一.单例模式的三种实现方式 1. 什么是单例模式 基于某种方法,实例化多次,得到同一个实例/对象 2. 为什么用单例模式 实例化多次,得到的对象属性内容都一样时,应该将这些对象指向同一个内存,即同一个实例,来节省内存空间 1. 实现单例模式方式一:类内部定义类方法实现 实现方法:类中定义了一个类方法 # 未单例模式前 import setting class Mysql: def __init__(self,ip,port): self.ip=ip self.port=port @classme

Python中的单例模式的几种实现方式的优缺点及优化

单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息.如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪

Python中的单例模式的几种实现方式的及优化

阅读目录(Content) 单例模式 实现单例模式的几种方式 1.使用模块 2.使用装饰器 3.使用类 4.基于__new__方法实现(推荐使用,方便) 5.基于metaclass方式实现 相关知识 实现单例模式 回到顶部(go to top) 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 比如,某个服务器程序的配置信息存放在一个文件中,客户

设计模式之单例模式(三种实现方式)

一.单例模式要点 1.单例,顾名思义,某个类仅仅能有一个实例. 2.它必须自行创建这个唯一的实例. 3.它必须自行向整个系统提供这个实例. 二.单例模式的三种实现 1.饿汉式单例类(类载入时就初始化) 代码实现 public class EagerSingleton { //私有的类成员常量 private static final EagerSingleton SINGLETON=new EagerSingleton(); //私有的默认构造方法.此类不能被继承 private EagerSi

单例模式的7种创建方式

1.饿汉式 public final class SingletonObject1 { private static final SingletonObject1 instance = new SingletonObject1(); private SingletonObject1() { } public static SingletonObject1 getInstance() { return instance; } } 饿汉式的创建方法关键在于 instance作为类变量直接得到了初始化

单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)

首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked locking)双重检查: 如果出于性能的考虑而需要对实例域(注意这个属性并没有被static修饰)使用延迟初始化,就使用双重检查模式 public class Singleton { private volatile Singleton uniqueInstance; private Singleton(){

设计模式:单例模式的三种创建方式及其各自的优缺点

单例模式: 确保一个类仅仅有一个实例,并提供全局訪问点.在Java中实现单例模式须要私有的构造器,一个静态方法和一个静态变量.确定在性能和资源上 的限制,怎样选择适当的方案来实现单例,以解决多线程的问题. 假设使用多个类载入器,可能导致单例失效产生多个实例.双重检查加锁不适用于1.4及更早版本号的java. 方式1: package singleton; /** * 同步getInstance()方法: * 有点:保证线程安全. * 缺点:性能大大下降 * @author Arvon * */

Item 3 ------单例模式的几种实现方式,及优缺点

单例模式,是指一个类只有一个唯一的实例,一个类只会被实例化一次.实现这种效果,最佳的方式,编写包含单个元素的枚举类型. 单例模式的最佳实现方式-----创建一个包含单个元素的枚举类 public enum Elvis { ONE_INSTANCE; public void leaveTheBuilding() { System. out.println("Whoa baby, I'm outta here!" ); } } -------------------------------