创建型-单例模式

冯旭光 Lv4

Java 中的单例模式是一种创建型设计模式,旨在确保一个类在整个应用程序的生命周期中只有一个实例。

在 Java 中,实现单例模式有多种方式,包括懒汉式、饿汉式、静态内部类、枚举和双重校验锁等。每种方式都有其优势和适用场景。

饿汉式

静态成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 饿汉式: 静态成员变量
*/
public class Singleton {

// 1. 私有构造方法
private Singleton() {}

// 2. 在本类中创建本类对象
private static Singleton instance = new Singleton();

// 3. 提供一个公共的访问方式, 让外界获取该对象
public static Singleton getInstance() {
return instance;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
10
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();

// 判断获取到的两个对象是否是同一个
System.out.println(instance == instance1); // 输出: true
}
}

总结

  • 缺点:浪费内存

静态代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 饿汉式: 静态代码块
* <p>存在问题: 浪费内存</p>
*/
public class Singleton {

private Singleton() {}

// 声明 Singleton 类型的变量
private static Singleton instance;

// 在静态代码块中进行赋值
static {
instance = new Singleton();
}

public static Singleton getInstance() {
return instance;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();

System.out.println(instance == instance1); // 输出: true
}
}

总结

  • 缺点:浪费内存

枚举类

枚举类型属于饿汉式模式

枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。其他方式都有可能被反射、序列化方式等破坏。

1
2
3
4
5
public enum Singleton {

INSTANCE;

}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;

System.out.println(instance == instance1); // 输出: true
}
}

懒汉式

synchronized 方法加锁(线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 懒汉式: 线程安全
*/
public class Singleton {

// 私有构造方法
private Singleton() {}

// 声明 Singleton 类型的变量 instance
private static Singleton instance;

// 对外提供访问方式
public static synchronized Singleton getInstance() {
// 判断 instance 是否为 null, 如果为 null, 说明还没有创建 Singleton 类的对象
// 如果没有, 创建一个并返回, 如果有, 直接返回
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();

System.out.println(instance == instance1); // 输出: true
}
}

总结

  • 缺点:synchronized 加在方法上,性能较差。

双重检查锁(线程安全)

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是 JVM 在实例化对象的时候会进行优化和指令重排操作。要解决双重检查锁模式带来的空指针异常的问题,只需要使用 volatile 关键字,volatile 关键字可以保证可见性和有序性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 懒汉式: 双重检查锁(线程安全)
*/
public class Singleton {

private Singleton() {}

private static volatile Singleton instance;

public static Singleton getInstance() {
// 第一次判断, 如果 instance 不为 null, 不进入抢锁阶段, 直接返回实例
if (instance == null) {
synchronized (Singleton.class) {
// 抢到锁之后再次判断是否为 null
if (instance == null) {
instance = new Singleton();
}
}
}

return instance;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();

System.out.println(instance == instance1); // 输出: true
}
}

静态内部类(线程安全)

静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

说明: 第一次加载 Singleton 类时不会去初始化 INSTANCE,只有第一次调用 getInstance,虚拟机加载 SingletonHolder 并初始化 INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 懒汉式: 静态内部类
*/
public class Singleton {

private Singleton() {}

private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}
}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client {

public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();

System.out.println(instance == instance1); // 输出: true
}
}
  • 标题: 创建型-单例模式
  • 作者: 冯旭光
  • 创建于 : 2024-02-19 15:39:25
  • 更新于 : 2025-03-11 14:43:45
  • 链接: https://blog.fengxuguang.top/posts/545bbc59/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论