目录
前言
一、饿汉模式
1.什么是饿汉模式
2.代码展示
3.优缺点
二、懒汉模式
1.什么是懒汉模式
2.代码展示
3..注意事项
三、静态内部类
1.如何使用静态内部类
2.代码展示
四、枚举
1.如何使用枚举
2.代码展示
总结
前言
单例模式常见写法有 4 种:饿汉模式、懒汉模式、静态内部类和枚举,接下来我们一一来看。
一、饿汉模式
1.什么是饿汉模式
饿汉模式也叫预加载模式,它是在类加载时直接创建并初始化单例对象,所以它并不存在线程安全的问题。它是依靠 ClassLoader 类机制,在程序启动时只加载一次,因此不存在线程安全问题。
2.代码展示
public class StarvingMode {
// 是线程安全的
// 类加载的时候执行
// JVM 保证了类加载的过程是线程安全的
private static StarvingMode instance = new StarvingMode();
public static StarvingMode getInstance() {
return instance;
}
private StarvingMode() {}
}
3.优缺点
优点:实现简单、不存在线程安全问题。
缺点:类加载时就创建了对象,如果之后没有被使用,就造成了资源浪费的情况。
二、懒汉模式
1.什么是懒汉模式
懒汉模式与饿汉模式相反,只有在第一次被使用的时候,才会被初始化,但是这样会存在线程安全问题。
2.代码展示public class LazyModeV3 { private volatile static LazyModeV3 instance = null; public static LazyModeV3 getInstance() { // 第一次调用这个方法时,说明我们应该实例化对象了 if (instance == null) { // 只有 instance 还没有初始化时,才会走到这个分支 // 这里没有锁保护,所以理论上可以有很多线程同时走到这个分支 synchronized (LazyModeV3.class) { // 通过上面的条件, // 让争抢锁的动作只在 instance // 实例化之前才可能发生。 // 实例化之后就不再可能 // 加锁之后才能执行 // 第一个抢到锁的线程,看到的 instance 是 null // 其他抢到锁的线程,看到的 instance 不是 null // 保证了 instance 只会被实例化一次 if (instance == null) { instance = new LazyModeV3(); // 只在第一次的时候执行 // 当重排序成 1 -> 3 -> 2 的时候可能出问题 // 通过 volatile 修复 } } } return instance; } private LazyModeV3() {} }
3..注意事项
1.这里使用volatile修饰对象,是为了防止在实例化对象时,发生代码重排序,导致线程不安全。加上volatile关键字时,实例化对象时,不会使其代码重排序。
2.在判断if(instance == null) 时,这里会出现 check-update 场景,会导致他的原子性发生破坏,所以,使用synchronized锁,对其进行保护,以维护他的原子性,从而保证线程安全。
三、静态内部类
1.如何使用静态内部类
静态内部类既能保证线程安全,又能保证懒加载,它只有在被调用时,才会通过ClassLoader机制来加载和初始化内部静态类,因此它是线程安全的。
2.代码展示public class Singleton { // 1.防止外部直接 new 对象破坏单例模式 private Singleton() { } // 2.静态内部类 private static class SingletonHolder { private static final Singleton instance = new Singleton(); } // 3.提供公共获取单例对象的方法 public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
四、枚举
1.如何使用枚举
枚举也是在第一次被使用时,才会被 Java 虚拟机加载并初始化,所以它也是线程安全的,且是懒加载的。
2.代码展示public enum EnumSingleton { instance; public EnumSingleton getInstance(){ return instance; } }
总结
单例模式适用于经常被访问的对象,或是创建和销毁需要调用大量资源和时间的对象,使用单例模式可以避免频繁创建和销毁对象。单例模式的常用实现方法有 4 种:饿汉模式、懒汉模式、静态内部类和枚举。