本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Java 实现单例模式
前置了解
-
单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个访问它的全局访问点。 -
单例模式下的类都是
只创建一个唯一实例的。 -
饿汉单例模式:在程序开始运行时,就将该单例对象加载到内存,即预先加载。 -
懒汉单例模式:使用到这个类时才创建实例,即懒加载。
破坏单例模式
- 无论是通过懒汉式还是饿汉式实现的单例模式,都可能通过
反射和反序列化破坏掉单例的特性,可以创建多个对象。
反射破坏单例模式
-
反射破坏单例模式: 利用反射,可以强制访问单例类的私有构造器,创建新的对象。 -
原理:通过
调用构造方法生成新的对象。 -
阻止方案:在
构造方法中添加 if 判空,满足实例不存在才创建实例。
反序列化破坏单例模式
-
反序列化破坏单例模式: 通过 readObject 方法读取对象时会返回一个新的对象实例。 -
原理:单例类
实现了序列化接口 Serializable, 就可以通过反序列化破坏单例;默认的反序列化方法 readObject 会返回一个新的对象实例。 -
阻止方案 1:不需要序列化时,就不
实现序列化接口 Serializable。 -
阻止方案 2:
重写反序列化方法 readObject,让其直接返回单例实例对象。
总结
-
注意:在整个单例模式实现中,统一用
getInstance 方法来获取该类的单例实例。 -
构造方法都是私有的(private)。 -
static表示 共享,final表示不可变,private表示私有。
饿汉单例模式
-
优点:没有加锁,执行效率高;线程安全。 -
缺点:类加载就初始化,getInstance 方法只是将对象输出,浪费内存。 -
直接创建一个
静态不可变常量来存储类实例,在声明时直接赋值。
懒汉单例模式
共有的优点:实例在调用 getInstance 方法时才会创建实例,这样是不占内存的。
普通懒汉模式
-
优点:没有加锁,执行效率高。 -
缺点:线程不安全。 -
创建一个
静态常量来存储类实例,初始值为 null。 -
用
if来判断是否为 null,是则 创建类实例,否则 无操作。
加了同步锁的懒汉模式
-
优点:线程安全。 -
缺点:锁的粒度太大,影响了程序的执行效率,效率低。 -
创建一个
静态常量来存储类实例,初始值为 null。 -
用
if来判断是否为 null,是则 创建类实例,否则 无操作。 -
在 getInstance 方法上
加 synchronized 锁。 -
这样
线程就必须排队使用 getInstance 方法。
使用类锁的懒汉模式
-
优点:线程安全;使用 synchronized 代码块优化执行时间,减少锁的粒度。 -
创建一个
静态常量来存储类实例,初始值为 null。 -
用
if来判断是否为 null,是则 创建类实例,否则 无操作。 -
第一个
if 判空用来阻止实例创建完成后,后续的线程进入 创建类实例流程。 -
第二个
if 判空用来阻止实例创建完成后,前面在 synchronized 代码块排队的线程进入 创建类实例流程。 -
synchronized 代码块是方法 synchronized 的优化版,在关键代码位置添加,功能上没有差别,但效率却提高了。
使用内部类的懒汉模式
-
优点:没有加锁,执行效率高;线程安全;不依赖 JDK 版本;外部类加载时,并不会加载内部类实例,而是在调用 getInstance 方法时才会new 内部类。 -
缺点:是通过虚拟机来保证方法使用锁,来保证线程安全,会增加 JVM 压力。 -
类实例存储在
类的私有内部类的 静态不可变常量中,即单例持有者是:内部类。 -
内部类的 静态不可变常量的创建是在第一次调用 getInstance 方法时开始的。 -
而
内部类在实现中是没有创建的,因为不需要,如果要创建,那是在第一次调用 getInstance 方法时开始创建。 -
静态内部类模式创建单例类实例是
使用 JVM 机制保证线程安全。
使用枚举类的懒汉模式
-
优点:唯一一种不会被破坏的单例实现模式;线程安全;没有加锁,执行效率高;外部类加载时,并不会加载内部枚举类实例,而是在调用 getInstance 方法时才会new 内部枚举类。 -
缺点:JDK 1.5 后才能使用。 -
枚举实例只能装载一次,同样,实例的属性也不会变。 -
类实例存储在
类的私有内部枚举类的 属性中,即单例持有者是:内部枚举类。 -
内部枚举类的创建是在第一次调用内部枚举类的 getInstance 方法时开始的。 -
不需要额外的操作即可
防止破环单例模式。
实现
饿汉单例模式
- 类代码
class HungrySingletonMode {
private static final HungrySingletonMode HUNGRY_SINGLETON_MODE = new HungrySingletonMode();
private HungrySingletonMode() {
}
public static HungrySingletonMode getInstance() {
return HUNGRY_SINGLETON_MODE;
}
public String getClassIntroduce() {
return "className:" + HungrySingletonMode.class + ";Introduce:普通的饿汉单例模式";
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
HungrySingletonMode hungrySingletonMode = HungrySingletonMode.getInstance();
System.out.println(hungrySingletonMode.getClassIntroduce());
}
}
懒汉单例模式(普通实现)
- 类代码
class LazySingletonMode {
private static LazySingletonMode LAZY_SINGLETON_MODE = null;
private LazySingletonMode() {
}
public static LazySingletonMode getInstance() {
// 第一次加载时才创建
if(LAZY_SINGLETON_MODE == null) {
LAZY_SINGLETON_MODE = new LazySingletonMode();
}
return LAZY_SINGLETON_MODE;
}
public String getClassIntroduce() {
return "className:" + LazySingletonMode.class + ";Introduce:普通的懒汉单例模式";
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
LazySingletonMode lazySingletonMode = LazySingletonMode.getInstance();
System.out.println(lazySingletonMode.getClassIntroduce());
}
}
懒汉单例模式(同步锁实现)
- 类代码
class LazySingletonMode1 {
private static LazySingletonMode1 LAZY_SINGLETON_MODE_1 = null;
private LazySingletonMode1() {
}
public static synchronized LazySingletonMode1 getInstance() {
// 在 LazySingletonMode 的基础上,加了 synchronized(同步锁)
if(LAZY_SINGLETON_MODE_1 == null) {
LAZY_SINGLETON_MODE_1 = new LazySingletonMode1();
}
return LAZY_SINGLETON_MODE_1;
}
public String getClassIntroduce() {
return "className:" + LazySingletonMode1.class + ";Introduce:加了同步锁的懒汉单例模式";
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
LazySingletonMode1 lazySingletonMode1 = LazySingletonMode1.getInstance();
System.out.println(lazySingletonMode1.getClassIntroduce());
}
}
懒汉单例模式(类锁实现)
- 类代码
class LazySingletonMode2 {
// 添加 volatile 是为了禁止 指令重排,解决 指令重排 问题。
// volatile 是 JVM 提供的轻量级同步机制
// 作用是: 保证可见性;禁止指令重排;但不保证原子性
private static volatile LazySingletonMode2 LAZY_SINGLETON_MODE_2 = null;
private LazySingletonMode2() {
}
public static LazySingletonMode2 getInstance() {
// 在 LazySingletonMode1 的基础上,将 synchronized(同步锁)改为:使用 synchronized 声明的方法
// 双重检验首先判断实例是否为空,然后使用 synchronized (class) 使用类锁,锁住整个类。
// 执行完代码块的代码之后,新建了实例后,因为第二重 if 其他代码都不走 if () 里面,只会在最开始的时候效率变慢。
if(LAZY_SINGLETON_MODE_2 == null) {
synchronized(LazySingletonMode2.class) {
if(LAZY_SINGLETON_MODE_2 == null) {
LAZY_SINGLETON_MODE_2 = new LazySingletonMode2();
}
}
}
return LAZY_SINGLETON_MODE_2;
}
public String getClassIntroduce() {
return "className:" + LazySingletonMode2.class + ";Introduce:使用类锁的懒汉单例模式";
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
LazySingletonMode2 lazySingletonMode2 = LazySingletonMode2.getInstance();
System.out.println(lazySingletonMode2.getClassIntroduce());
}
}
懒汉单例模式(内部类实现)
- 类代码
class LazySingletonMode3 {
private LazySingletonMode3() {
System.out.println("className:" + LazySingletonMode3.class + " 创建了!");
}
public static LazySingletonMode3 getInstance() {
return SingletonHolder.LAZY_SINGLETON_MODE_3;
}
public String getClassIntroduce() {
return "className:" + LazySingletonMode3.class + ";Introduce:使用内部类的懒汉单例模式";
}
/**
* 内部类
*/
public static class SingletonHolder {
private static final LazySingletonMode3 LAZY_SINGLETON_MODE_3 = new LazySingletonMode3();
public SingletonHolder() {
System.out.println("className:" + SingletonHolder.class + " 创建了!");
}
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
LazySingletonMode3 lazySingletonMode3 = LazySingletonMode3.getInstance();
System.out.println(lazySingletonMode3.getClassIntroduce());
}
}
懒汉单例模式(枚举实现)
- 类代码
class LazySingletonMode4 {
private LazySingletonMode4() {
System.out.println("className:" + LazySingletonMode4.class + " 创建了!");
}
public static LazySingletonMode4 getInstance() {
return SingletonHolder.INSTANCE.getInstance();
}
public String getClassIntroduce() {
return "className:" + LazySingletonMode4.class + ";Introduce:使用内部类的懒汉单例模式";
}
/**
* 枚举
* <p>
* 枚举类型是线程安全的,并且只会装载一次
*/
private enum SingletonHolder {
/**
* 表示实例,每一个枚举只会装载一次
*/
INSTANCE;
private final LazySingletonMode4 instance;
SingletonHolder() {
this.instance = new LazySingletonMode4();
System.out.println("className:" + SingletonHolder.class + " 创建了!");
}
private LazySingletonMode4 getInstance() {
return instance;
}
}
}
- 怎么获取实例?
public class Main {
public static void main(String[] args) {
LazySingletonMode4 lazySingletonMode4 = LazySingletonMode4.getInstance();
System.out.println(lazySingletonMode4.getClassIntroduce());
}
}
引用与参考
-
文章参考:单例模式的六种实现方式!
-
文章参考:为什么用枚举类来实现单例模式越来越流行?
-
文章参考:5种方式实现 Java 单例模式