Java中常用的线程安全单例模式实现

在 Java 中,实现线程安全的单例模式有多种方式。以下是几种常用的线程安全单例模式实现:


1. 饿汉式(线程安全,类加载时初始化)

特点:简单且线程安全,但如果实例过于占用资源且程序可能不使用它,会造成内存浪费。

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
​
    // 私有构造方法,防止外部创建实例
    private Singleton() {}
​
    // 全局访问点
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
  • 优点:实现简单,类加载时就完成实例化,线程安全。

  • 缺点:即使不使用也会实例化,占用资源。


2. 懒汉式(线程不安全,延迟加载)

特点:实例在需要时创建,但默认实现线程不安全。

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
​
    // 私有构造方法,防止外部创建实例
    private Singleton() {}
​
    // 全局访问点
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
  • 缺点:在多线程环境中可能会创建多个实例,不推荐在并发场景下使用。


3. 懒汉式 + synchronized(线程安全,但性能较差)

特点:通过 synchronized 关键字确保线程安全,但性能较低。

public class Singleton {
    private static Singleton instance;
​
    private Singleton() {}
​
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 优点:线程安全,延迟加载。

  • 缺点synchronized 会影响性能,每次调用都要获取锁。


4. 双重检查锁(推荐,性能较高)

特点:结合延迟加载与线程安全,减少同步开销。

public class Singleton {
    // 使用 volatile 确保多线程下的可见性和禁止指令重排序
    private static volatile Singleton instance;
​
    private Singleton() {}
​
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 优点:线程安全,延迟加载,性能较好。

  • 缺点:实现稍显复杂。


5. 静态内部类(推荐,线程安全,延迟加载)

特点:利用类加载机制实现延迟加载,同时天然线程安全。

public class Singleton {
    private Singleton() {}
​
    // 静态内部类,在加载时初始化 Singleton 实例
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
​
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}
  • 优点

    1. 延迟加载:Holder 类只有在第一次调用 getInstance 方法时才会加载并初始化实例。

    2. 线程安全:JVM 类加载机制保证线程安全。


6. 枚举单例(最简单,线程安全)

特点:利用枚举的特性实现单例。

public enum Singleton {
    // 定义一个枚举元素,代表 Singleton 的唯一实例
    INSTANCE;

    // 单例类中的其他方法或变量
    private int value;

    // 初始化方法,可以在枚举的构造方法中进行初始化
    Singleton() {
        this.value = 42; // 假设默认值为 42
    }

    // 对外暴露的方法,可以操作或获取内部状态
    public void someMethod() {
        System.out.println("Singleton method invoked. Current value: " + value);
    }

    // Getter 和 Setter 方法
    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}
public class Main {
    public static void main(String[] args) {
        // 获取单例实例
        Singleton instance = Singleton.INSTANCE;

        // 调用方法
        instance.someMethod(); // 输出: Singleton method invoked. Current value: 42

        // 修改状态
        instance.setValue(100);
        System.out.println("Updated value: " + instance.getValue()); // 输出: Updated value: 100
    }
}

  

  • 优点

    1. 线程安全:枚举本身是线程安全的。

    2. 防止反序列化创建新实例:枚举类型保证了单例。

  • 缺点:不能进行懒加载,但推荐作为一种标准单例实现方式。


总结

实现方式是否线程安全是否延迟加载优点缺点
饿汉式 简单,线程安全 不使用时会浪费资源
懒汉式 延迟加载 多线程环境可能不安全
synchronized 懒汉式 简单,线程安全 性能差
双重检查锁 性能较好,线程安全 实现复杂
静态内部类 延迟加载,线程安全,推荐使用 适用性广,缺点少
枚举单例 简洁,线程安全,防反序列化和反射攻击 不能延迟加载

推荐实现

  • 常规场景:使用 静态内部类枚举单例,线程安全且性能优越。

  • 对性能要求极高:可以使用 双重检查锁 实现。

posted @ 2024-11-21 10:43  luorx  阅读(1)  评论(0编辑  收藏  举报