深度解析单例模式

一、概述

1.1 特点

  1. 单例类。只有一个实例对象
  2. 该单例对象必须由单例类自行创建
  3. 单例类对外提供一个访问该单例的全局访问点

1.2 优缺点

优点:

  1. 保证内存里只有一个实例,减少了内存的开销
  2. 可以避免对资源的多重占用

缺点:

  1. 没有接口,扩展困难。违背开闭原则
  2. 不利于代码调试。调试过程中,如果单例代码没有执行完,不能创建一个对象

1.3 应用场景

  1. 频繁创建类,使用单例可以降低系统的内存压力,减少GC。
  2. 全局信息类。利用一个类记录网站的访问次数,不希望有的访问被记录在对象A上,有的记录在对象B上。

二、八种经典实现方式

2.1 饿汉式

代码实现:

public class Hungry {
	private Hungry(){};
	// 1. 静态变量 
    // private static Hungry hungry = new Hungry(); 
    // 2. 静态代码块 private final static Hungry hungry; 
    static { hungry = new Hungry(); } 
    public static Hungry getInstance() { return hungry; } 
}

优点: 类加载时,就把实例初始化。
缺点: 浪费内存空间。

2.2 懒汉式

代码实现:

// 1. 线程不安全
// 2. 线程安全,synchronized修饰方法
// 3. 线程安全,synchronized代码块修饰
public class Lazy {
    private Lazy(){
    }
    // 1. 多线程不安全
    private static Lazy lazy = null;
    public Lazy getInstance() {
        if (lazy == null) {
            lazy = new Lazy();
        }
        return lazy;
    }
    // 2. 线程安全,synchronized修饰方法。多线程无法并行处理,效率低下
    public synchronized Lazy getInstance() {
        if (lazy == null) {
            lazy = new Lazy();
        }
        return lazy;
    }
    // 3. 线程安全,synchronized代码块修饰. 产生多个实例,即多个线程都通过lazy==null的判断
    public static Lazy getInstance(){
        if (lazy == null) {
            synchronized (Lazy.class){
                lazy = new Lazy();
            }
        }
        return lazy;
    }    

适用场景: 如果程序一开始要加载的资源太多,可以使用懒加载。

2.3 双重检查方式

代码实现:

// 1. 双重检测1:线程安全,效率较高,延迟加载;
// 但存在指令重排,即创建对象需要3个步骤,3个步骤之间可能会重排,导致空指针
// 异常
// 2. 双重检测2:利用volatile修饰的变量;volatile可以防止指令重排
public class DoubleCheck {
    private DoubleCheck(){
    }
    // 1. 双重检测1 
    // private static DoubleCheck doubleCheck;
    // 2. 双重检测2
    private volatile static DoubleCheck doubleCheck;
    public static DoubleCheck getInstance() {
        if (doubleCheck == null) {
            synchronized (DoubleCheck.class) {
                if (doubleCheck == null) {
                    doubleCheck = new DoubleCheck();
                }
            }
        }
        return doubleCheck;
    }
}
优点:线程安全;延迟加载;效率较高

2.4 静态内部类方式

public class Inner {
    private Inner(){
    }
    public static Inner getInstance() {
        return InnerClass.inner;
    }
    private static class InnerClass{
        private static final Inner inner = new Inner();
    }
}
静态内部类方式在Inner类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载
InnerClass类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化
时,别的线程是无法进入的。
【优点】:避免了线程不安全,延迟加载,效率高

2.5 枚举类方式

// 最安全
public enum EnumSingle {
    INSTANCE;
    public static EnumSingle getInstance(){
        return INSTANCE;
    }
}
优点:
1. 写法简单
2. 线程安全
3. 避免反序列化破坏单例

相关推荐

  1. 深度解析模式

    2024-03-25 08:02:03       41 阅读
  2. 模式模板

    2024-03-25 08:02:03       43 阅读
  3. 模式场景模拟和问题解决

    2024-03-25 08:02:03       26 阅读
  4. 模式【C#】

    2024-03-25 08:02:03       54 阅读
  5. python模式

    2024-03-25 08:02:03       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-25 08:02:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-25 08:02:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-25 08:02:03       87 阅读
  4. Python语言-面向对象

    2024-03-25 08:02:03       96 阅读

热门阅读

  1. NPM 国内镜像

    2024-03-25 08:02:03       34 阅读
  2. ubantu22.04安装各种配置

    2024-03-25 08:02:03       47 阅读
  3. ARM:串口控制点灯

    2024-03-25 08:02:03       43 阅读
  4. 如何一键升级 package.json 下所有依赖的版本

    2024-03-25 08:02:03       42 阅读
  5. 项目管理-需求分析

    2024-03-25 08:02:03       40 阅读
  6. 企业怎么申请保密资质?

    2024-03-25 08:02:03       42 阅读
  7. HTML:常用标签

    2024-03-25 08:02:03       40 阅读
  8. Golang获取音视频时长信息

    2024-03-25 08:02:03       46 阅读
  9. ffmpeg/ffplay指令

    2024-03-25 08:02:03       46 阅读
  10. 找不到北的i++

    2024-03-25 08:02:03       39 阅读