单例模式定义
在软件系统中, 一个类只有一个实例对象.(该类只提供一个取得实例的静态方法)
推荐使用的三种单例模式
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());
- }
- }
参考文档:
单例模式的八种写法比较
来源: https://www.cnblogs.com/noneplus/p/11336752.html