# 概念

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。

# 三种单例模式

  • 饿汉式

系统加载时初始化实例,即使不加载也会初始化,占用内存较大,线程安全

  • 懒汉式

系统加载不初始化,需要加载实例时再初始化实例,线程不安全,加了双重检查之后线程安全

  • 枚举

JDK1.5 加入的,也算是最推荐使用的方法,兼顾内存跟线程安全

# 举个栗子

# 懒汉式

a
/**
 * 单例模式 (饿汉式)
 */
public final class HungerSingleton {
    /**
     * 杜绝外面直接 new 只有一种获取方式
     */
    private HungerSingleton() {}
    /**
     * 实例化
     */
    private static final HungerSingleton INSTANCE = new HungerSingleton();
    /**
     * 获取实例
     */
    public static HungerSingleton getInstance() {
        return INSTANCE;
    }
}
  • 优点:单例占用内存比较小,初始化时就会被用到的情况。
  • 缺点:单例占用的内存比较大,或单例只是在某个特定场景下才会用到

# 懒汉式

a
/**
 * 单例模式 (懒汉式)
 */
public final class LazySingleton {
    /**
     * 杜绝外面直接 new 只有一种获取方式
     */
    private LazySingleton() {}
    /**
     * 实例化
     */
    private static volatile LazySingleton INSTANCE;
    /**
     * 获取实例
     */
    public static LazySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new LazySingleton();
        }
        return INSTANCE;
    }
}
  • 优点:内存节省,由于此种模式的实例实在需要时创建,如果某次的程序运行没有用到,就是可以节省内存
  • 缺点:线程不安全,分析见下面问题
a
/**
 * 单例模式 (懒汉式)
 */
public final class LazySingleton {
    /**
     * 杜绝外面直接 new 只有一种获取方式
     */
    private LazySingleton() {}
    /**
     * 实例化
     */
    private static volatile LazySingleton INSTANCE;
    /**
     * 获取实例(双重检查)
     */
    public static LazySingleton getInstance() {
        if (INSTANCE == null){
            synchronized (LazySingleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new LazySingleton();
                }
            }
        }
        return INSTANCE;
    }
}
  • 优点:多线程安全

  • 缺点:执行效率低,每个线程在想获得类的实例时候,执行 getInstance () 方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。方法进行同步效率太低要改进。

a
/**
 * 枚举是天然单例
 */
public enum EnumSingleton {
    INSTANCE;
    @Override
    public String toString() {
        return super.toString();
    }
}
  • 优点:兼顾内存和多线程安全
  • 缺点:为啥没有早点遇到你(1.5 版本之后更新)

# 问题

为什么要考虑线程安全?

# 举个栗子(懒汉式非双重检查)

步骤线程 1线程 2
1getInstance()
2getInstance()
3if (INSTANCE == null)
4if (INSTANCE == null)
5INSTANCE = new Singleton();
6return INSTANCE;
7INSTANCE = new Singleton();
8return INSTANCE;

这里就发生的线程安全的问题,1、2 两个步骤分别由线程 1、2 进入 getInstance () 的方法,然后 3、4 两个步骤同时通过,因为这个时候确实还没有实例化为 null,所以后面就会线程 1 new 一个,线程 2 也 new 一个 INSTANCE,这样就违背了单例的原则,所以考虑线程安全还是有必要的。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

vayi 微信支付

微信支付

vayi 支付宝

支付宝

vayi 贝宝

贝宝