创建型模式————单例模式(2.1)

  • 2019 年 10 月 3 日
  • 筆記

单例模式定义

在软件系统中,一个类只有一个实例对象。(该类只提供一个取得实例的静态方法)

推荐使用的三种单例模式

  • DoubleCheck
  • 静态内部类
  • 枚举

1.DoubleCheck 双重检查

特点:效率高,线程安全,延迟加载。

class DoubleCheck  {      private static volatile DoubleCheck instance;        private DoubleCheck(){}        public static DoubleCheck getInstance()      {          /*          DoubleCheck如何实现?线程安全和效率提升            在多线程的环境下,假设线程A直接进入#2,实例化对象。          且实例化方法外用synchronized修饰,所以是线程安全的。          当线程A实例化对象结束,对象instance已经被创建,执行到#1的线程将会直接调到#3,返回instance            且DoubleCheck实现了延迟加载(new在方法里)           */          if(instance==null)                               //#1          {              synchronized (DoubleCheck.class)             //#2              {                  if(instance==null)                  {                      instance = new DoubleCheck();        //#3                  }              }          }          return instance;      }  }      public class Operation  {      public static void main(String[] args) {          DoubleCheck doubleCheck1 = DoubleCheck.getInstance();          DoubleCheck doubleCheck2 = DoubleCheck.getInstance();            System.out.println(doubleCheck1.hashCode());          System.out.println(doubleCheck2.hashCode());          }  }

2.静态内部类

特点:通过JVM类加载避免了线程安全问题,延迟加载,效率高。

class StaticClassInner {        private StaticClassInner() {}            /*       使用静态内部类,实现了延迟加载       调用getInstance()方法时,才会加载StaticClassInnerInstance。         通过JVM类加载线程安全的机制,避免了线程不安全。         */      private static class StaticClassInnerInstance {          private static final StaticClassInner INSTANCE = new StaticClassInner();      }        public static StaticClassInner getInstance() {          return StaticClassInnerInstance.INSTANCE;      }  }    public class Operation  {      public static void main(String[] args) {          StaticClassInner doubleCheck1 = StaticClassInner.getInstance();          StaticClassInner doubleCheck2 = StaticClassInner.getInstance();            System.out.println(doubleCheck1.hashCode());          System.out.println(doubleCheck2.hashCode());          }  }

3.枚举

Effective Java作者Josh Bloch推荐。

enum Hq  {      INSTANCE;        public void printf()      {          System.out.println("ins");      }  }    public class Operation  {      public static void main(String[] args) {          Hq hq = Hq.INSTANCE;          Hq hqq = Hq.INSTANCE;            System.out.println(hq.hashCode());          System.out.println(hqq.hashCode());      }  }

饿汉式的延迟加载问题(可用)

如果创建的对象一定会被使用,那么可以忽略内存浪费的问题。

class SingleTom  {      private SingleTom()      { }        /*      在静态常量中实例化对象,无法实现延迟加载,如果对象未被使用,会造成内存浪费。(#1)      无线程安全问题        将实例化对象放于静态代码块中并无实际作用       */      private final static SingleTom instance;  // = new SingleTom();#1        static  //#2      {          instance=new SingleTom();      }          public static SingleTom getInstance()      {          return instance;      }  }    public class Operation  {      public static void main(String[] args) {          SingleTom hq = SingleTom.getInstance();          SingleTom hqq = SingleTom.getInstance();            System.out.println(hq.hashCode());          System.out.println(hqq.hashCode());      }  }

懒汉式的线程安全问题(不可用)

class Singletom {        private static Singletom singleton;        private Singletom() {}        /*      线程不安全:        调用getInsyance()方法时,如果同时有多个线程同时进入到#1      就会创建多个实例对象        倘若在方法上加上syn关键字,线程同步问题解决,但效率大大降低        doublecheck大概就是在这种纠结下选择用两次if(singleton == null)来控制线程同步和效率问题         */      public static /*synchronized*/ Singletom getInstance() {          if (singleton == null) {                   //#1              singleton = new Singletom();          }          return singleton;      }  }    public class Operation  {      public static void main(String[] args) {          Singletom hq = Singletom.getInstance();          Singletom hqq = Singletom.getInstance();            System.out.println(hq.hashCode());          System.out.println(hqq.hashCode());      }  }

参考文档:

单例模式的八种写法比较