单例模式

2016年11月21日

介绍

即在整个应用程序运行过程中, 只应该存在一个实例的类, 则适合使用单例模式.

解决问题: 避免一个全局使用的类被频繁的创建与销毁

例子

单例模式的几种不同的使用方式

饿汉模式

即在应用程序初始化时,就载入单例服务,不考虑服务是否会真的被使用.

这个模式是线程安全的, 类的静态属性的初始化由 JVM 来保证原子性和可见性

    public class Singleton {

        private Singleton(){};

        private static final Service service = new Service();

        public static Service getInstance() {
            return service;
        }

    }

懒汉模式

即 在第一次调用时才初始化所需要的单例类, 可避免 使用饿汉模式,导致服务并未被使用,却被提前加载耗费资源的情况.

懒汉模式的实现有几种, 下面我按照推崇程度的写法依次说明.

使用 静态内部类 实现懒加载

这种方法,实现简单, 无需额外的加锁等操作, 原子性和可见性由 JVM 来保证.

    public class Singleton {

        private Singleton(){};

        public static Service getInstance() {
            return SingletonHolder.INSTANCE;
        }

        private static class SingletonHolder {
            private static final Service INSTANCE = new Service();
        }

    }

使用 自己加锁的方式 实现懒加载

这种方式,也能保证线程安全性,实现也比较简单,但是由于每一次获取 Service 时都会进行加解锁过程,从而导致产生大量无用的加解锁操作,降低了程序性能.

    public class Singleton {

        private Singleton(){};

        private static Service service;

        public static synchronized Service getInstance() {

            if (null == service) {
                service = new Service();
            }

            return service;
        }

    }

使用 DCL (Double Check Lock) 实现懒加载

注意这里需要将 单例服务 设置为 volatile 从而阻止指令重排序 ( 在java 1.4 及 之前的版本也是不起作用的 ). DCL这种方式就避免了上面的大量无用加解锁操作. 关于 DCL的说明(传送门)

      public class Singleton {

          private Singleton(){};

          private static volatile Service service;

          public static Service getInstance() {

              if (null == service) {
                  synchronized (Singleton.class) {
                      if (null == service) {
                          service = new Service();
                      }
                  }
              }

              return service;
          }

      }