创建型模式————单例模式(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()); } }
参考文档: