---
title: 线程安全单例
published: 2025-08-07
description: ''
image: ''
tags: [JUC,JAVA,volatile,线程安全,单例模式]
category: 'Java > JUC'
draft: false 
lang: ''
---


![](https://blog.meowrain.cn/api/i/2025/05/27/11a6ta3-0.webp)
# 1 解决反序列化导致的单例破坏现象

这里的单例问题是，如果对一个可序列化对象进行反序列化，会创建一个新的对象，这就违背了我们想要全局单例的目标。因此要重写readResolve方法。

![](https://blog.meowrain.cn/api/i/2025/05/26/sjvikb-0.webp)

```java
package org.example.sigletons;

import java.io.Serializable;

public class Singleton1 implements Serializable {
    private Singleton1(){}
    private static final Singleton1 INSTANCE = new Singleton1();
    public Singleton1 getInstance() {
        return INSTANCE;
    }
    public Object readResolve() {
        return INSTANCE;
    }
}

```

# 2 使用枚举实现单例模式
![](https://blog.meowrain.cn/api/i/2025/05/26/swwpe2-0.webp)

```java
package org.example.sigletons;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public enum Singleton2 {
    INSTANCE;
    private final Properties properties;
    private Singleton2() {
        properties = new Properties();
        String configFile = "application.properties";
        System.out.println("ConfigurationManager: Initializing and loading " + configFile);
        try(InputStream inputStream = Singleton2.class.getClassLoader().getResourceAsStream(configFile)){
            if(inputStream == null){
                System.out.println("ConfigurationManager: Sorry, unable to find " + configFile);
                // 在实际应用中，这里可能抛出异常或有更复杂的错误处理
                return;
            }
            properties.load(inputStream);
            System.out.println("ConfigurationManager: Configuration loaded successfully.");
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
    public String getProperty(String key) {
        return properties.getProperty(key);
    }
    public String getProperty(String key,String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }

    // 可以添加其他需要的方法，比如重新加载配置等（需要考虑线程安全）
    public void listProperties() {
        properties.forEach((key, value) -> System.out.println(key + "=" + value));
    }
}
class TestSingleton2 {
    public static void main(String[] args) {
        Singleton2 singleton2 = Singleton2.INSTANCE;
        singleton2.listProperties();
    }
}
```

![](https://blog.meowrain.cn/api/i/2025/05/26/swav0i-0.webp)


💎 枚举单例：全面解答这些问题！📜✨

枚举单例是一种非常推荐的单例实现方式，因为它不仅简单、易用，还天然地具备线程安全和防止反序列化、反射破坏单例的能力。接下来，我们重点针对 **枚举单例** 来回答这些问题！

---

### **问题 1：枚举单例是如何限制实例个数的？**

枚举单例通过枚举的机制天然地保证：
1. 枚举类的每一个枚举实例（如单例对象）都在 **类加载阶段** 就完成初始化，并且整个应用程序中只有一个实例。
2. 枚举类型底层由 JVM 的实现机制保证，它不像普通类那样允许通过反射或 `new` 额外创建实例。

#### 示例：
```java
public enum SingletonEnum {
    INSTANCE; // 枚举单例实例

    public void doSomething() {
        System.out.println("Doing something...");
    }
}
```

#### 使用方式：
即便通过 `SingletonEnum.INSTANCE` 多次获取，得到的始终是同一个实例。
```java
SingletonEnum instance1 = SingletonEnum.INSTANCE;
SingletonEnum instance2 = SingletonEnum.INSTANCE;
System.out.println(instance1 == instance2); // 输出：true
``` 

---

### **问题 2：枚举单例在创建时是否有并发问题？**

枚举单例天然线程安全，因为：
1. 枚举类型的初始化由 JVM 保证，是在类加载时完成的。
2. 类加载过程是线程安全的，JVM 使用了类加载的同步机制，保证枚举单例的初始化不会因多线程而发生竞争。

#### 举例：
即使多个线程同时调用 `SingletonEnum.INSTANCE`，它们都会得到在类加载阶段构造好的唯一对象，无需额外同步。

---

### **问题 3：枚举单例能否被反射破坏单例？**

**不会！** 枚举类型的结构特殊，无法被反射破坏单例。这是因为：
1. 枚举的构造器是私有的，并且其底层会检测反射调用。
2. 如果尝试通过反射显式调用枚举类的构造器，JVM 会抛出 `IllegalArgumentException`。

#### 验证代码：
```java
import java.lang.reflect.Constructor;

public class EnumReflectionTest {
    public static void main(String[] args) {
        try {
            // 获取枚举的构造器
            Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            SingletonEnum instance = constructor.newInstance(); // 反射创建枚举对象
        } catch (Exception e) {
            e.printStackTrace(); // 会抛出 IllegalArgumentException
        }
    }
}
```

#### 运行结果：
```
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
```

---

### **问题 4：枚举单例能否被反序列化破坏单例？**

枚举单例天然具备防止反序列化破坏的特性，原因是：
1. `Enum` 类型的序列化机制是由 JVM 内部实现的，不走普通的对象序列化流程。
2. 反序列化枚举对象时，JVM 会直接返回枚举类中的现有实例，而不是从序列化流中创建新对象。

#### 验证代码：
```java
import java.io.*;

public class EnumSerializationTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        SingletonEnum instance1 = SingletonEnum.INSTANCE;

        // 序列化枚举对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enum_singleton.obj"));
        oos.writeObject(instance1);
        oos.close();

        // 反序列化枚举对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enum_singleton.obj"));
        SingletonEnum instance2 = (SingletonEnum) ois.readObject();

        // 判断是否破坏单例
        System.out.println(instance1 == instance2); // 输出：true，单例没有破坏
    }
}
```

---

### **问题 5：枚举单例属于懒汉式还是饿汉式？**

**枚举单例本质上属于饿汉式单例**。它的特点是**在类加载阶段完成初始化**：
- 枚举的实例在类加载时就被创建并初始化。
- 即使程序中从未访问过 `SingletonEnum.INSTANCE`，枚举实例依然会被加载。

#### 优点：
- 线程安全，无需为单例初始化额外编写同步代码。
- 实现简洁，JVM 自动保证。

#### 缺点：
- 如果枚举实例较多，并且包含较大的初始化逻辑，会导致类加载阶段性能开销增加。

---

### **问题 6：枚举单例如果希望加入一些初始化逻辑，该如何做？**

可以通过添加枚举的构造方法和静态方法来实现初始化逻辑。枚举的构造方法是私有的，可以用来在实例创建时执行初始化。

#### 修改代码：
```java
public enum SingletonEnum {
    INSTANCE; // 枚举单例实例

    private String configuration;

    // 枚举的构造方法
    SingletonEnum() {
        // 初始化逻辑
        configuration = "System Configuration Loaded";
    }

    public String getConfiguration() {
        return configuration;
    }
}
```

#### 测试：
```java
public class TestEnumInitialization {
    public static void main(String[] args) {
        SingletonEnum instance = SingletonEnum.INSTANCE;
        System.out.println(instance.getConfiguration()); // 输出：System Configuration Loaded
    }
}
```

#### 分析：
- 枚举类型的构造器会在类加载时调用，且只调用一次。
- 可用枚举构造器实现单例实例的初始化逻辑。

---

### 总结

**为何枚举单例完美适合单例模式？**
- 它是天生线程安全的，JVM 保障了枚举实例的唯一性。
- 枚举实例不能通过反射或序列化破坏。
- 枚举的初始化流程天然符合饿汉式单例的特点。

---

# 3 Double Check

https://meowrain.cn/archives/volatile-shi-xian-dan-li-mo-shi-de-shuang-zhong-suo


```java
package cn.meowrain;

public class DoubleSingleton {
    private static volatile DoubleSingleton INSTANCE = null;
    public static DoubleSingleton getInstance() {
        if(INSTANCE != null) {
            return INSTANCE;
        }
        synchronized (DoubleSingleton.class){
            if(INSTANCE != null) {
                return INSTANCE;
            }
            INSTANCE = new DoubleSingleton();
            return INSTANCE;
        }
    }
}

```

![](https://blog.meowrain.cn/api/i/2025/05/27/10zdcs1-0.webp)


# 4 静态内部类懒汉式创建线程安全单例

```
package cn.meowrain;

public class Singleton2 {
    private Singleton2(){}
    // 问题1： 属于懒汉式还是饿汉式
    private static class LazyLoader{
        static final Singleton2 INSTANCE = new Singleton2();
    }
    // 在创建的时候是否有并发问题
    public static Singleton2 getInstance() {
        return  LazyLoader.INSTANCE;
    }
}

```
![](https://blog.meowrain.cn/api/i/2025/05/27/1132c75-0.webp)

![](https://blog.meowrain.cn/api/i/2025/05/27/1144xf0-0.webp)