美文网首页
设计模式-单例

设计模式-单例

作者: EvilsoulM | 来源:发表于2015-11-22 16:09 被阅读77次

    单例模式

    单例设计模式是设计模式中使用最普遍的模式之一。用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。

    单例带来的好处

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

    单例的实现

    单例模式的核心在于通过一个接口返回唯一对象的实例,最简单的例子:

    public class SimpleSingleInstance {
        private SimpleSingleInstance() { 
         System.out.println("SimpleSingleInstance");    
        }    
        private static SimpleSingleInstance instance = new SimpleSingleInstance();    
        public static SimpleSingleInstance getInstance() {
            return instance;
        }
    }
    

    这个例子实现方式非常简单,而且十分可靠。
    1.一个private 访问级别的构造函数,确保单例不会再系统中被实例化
    2.getInstance保证只能获取到一个唯一的单例对象

    但是这个类有个很明显的缺点,没有做到延迟加载,也就是说在jvm加载单例类的时候,单例对象就会被创建。

    public class SimpleSingleInstance {
        private SimpleSingleInstance() { 
         System.out.println("SimpleSingleInstance");    
        }    
        private static SimpleSingleInstance instance = new SimpleSingleInstance();    
        public static SimpleSingleInstance getInstance() {
            return instance;
        }
    }
      public static void log(){
          System.out.println("log");
      }
    

    当我们调用SimpleSingleInstance.log();的时候程序输出:

    SimpleSingleInstance
    log

    虽然我们没有使用单例类,但是还是被创建出来了,如果这个单例创建比较耗时的话我们在调用他的静态方法的时候,也就造成了该方法第一次的执行时间加长,为了解决这种问题我们引入了赖加载。

    public class LazySingleInstance {
        private LazySingleInstance() {
            System.out.println("LazySingleInstance");
        }    private static LazySingleInstance instance = null;
        public  synchronized static LazySingleInstance getInstance() {
                    if (instance == null) {
                        instance = new LazySingleInstance();
                    }
            return instance;
        }
    }
    

    首先将instance初始值为null,确保系统启动的时候不会加载,其次在getIntance判断instance是不是为null不为null采取创建对象,当然也加上了同步锁保障多线程同时访问不会创建出多个对象,也就违反了我们写单例的目的。

    使用上面的例子虽然实现了延迟加载,但是也加入的同步锁,再多线程中,它的耗时也会大大增加。

    for (int i = 0; i < 5; i++) {
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 100000; i++) {
                    long beginTime = System.currentTimeMillis();
                    SimpleSingleInstance.getInstance(); 
                      spendTime += (System.currentTimeMillis() - beginTime);
                    if (i == 100000 - 1) { 
                       System.out.println("SimpleSingleInstance spend:" + (spendTime));
                    }
                }
            }
        }.start();
    }
    

    我们开启五个线程去执行SimpleSingleInstance耗时

    SimpleSingleInstance
    SimpleSingleInstance spend:19
    SimpleSingleInstance spend:17
    SimpleSingleInstance spend:17
    SimpleSingleInstance spend:19
    SimpleSingleInstance spend:20
    

    我们开启五个线程去执行SimpleSingleInstance耗时

    LazySingleInstance
    LazySingleInstance spend:268
    LazySingleInstance spend:268
    LazySingleInstance spend:273
    LazySingleInstance spend:274
    LazySingleInstance spend:274
    

    开启五个线程同时执行,可以看出性能相差巨大。

    为了解决这个问题,我们对LazySingleInstance进行改造

    public class LazySingleInctanceV2 {
        private LazySingleInctanceV2() {
            System.out.println("LazySingleInctanceV2");
        }
        private volatile static LazySingleInctanceV2 instance = null;
        public static LazySingleInctanceV2 getInstance() {//去掉这里同步锁,在大部分情况下instance都不为null
            if (instance == null) {
                synchronized (LazySingleInctanceV2.class) {
                    if (instance == null) {//双重判断避免构建多个对象
                        instance = new LazySingleInctanceV2();
                    }
                }
            }
            return instance;
        }
    }
    
    LazySingleInctanceV2
    LazySingleInctanceV2 spend:28
    LazySingleInctanceV2 spend:29
    LazySingleInctanceV2 spend:29
    LazySingleInctanceV2 spend:29
    LazySingleInctanceV2 spend:29
    

    可以看出运行时间基本上和SimpleSingleInstance保持在了一个数量级

    当然还有一种更加简单的实现方式

    public class StaticSingleInstance {
        private StaticSingleInstance() {
            System.out.println("StaticSingleInstance");
        }
        public static StaticSingleInstance getInstance() {
            return SingleInstanceHolder.instance;
        }
        private static class SingleInstanceHolder {//内部类来维护单例的实例
            private static StaticSingleInstance instance = new StaticSingleInstance();
        }
    }
    

    在上面的实现中,使用内部类来维护单例的实例,当单例被加载的时候,其内部类并不会被初始化,当StaticSingleInstance类被加载入JVM的时候,不会初始化单例类,而当调用getInstance的时候才回去加载SingleInstanceHolder从而初始化instance。

    StaticSingleInstance
    StaticSingleInstance spend:22
    StaticSingleInstance spend:23
    StaticSingleInstance spend:23
    StaticSingleInstance spend:23
    StaticSingleInstance spend:24
    

    相关文章

      网友评论

          本文标题:设计模式-单例

          本文链接:https://www.haomeiwen.com/subject/mwqrhttx.html