Java单例模式深度解析:线程安全与性能优化

需积分: 0 0 下载量 76 浏览量 更新于2024-09-01 收藏 139KB PDF 举报
性能优化",允许指令重排序,也就是这三步不一定按照顺序执行。在多线程环境下,如果步骤2和3发生了重排序,就会导致线程安全问题。具体来说,假设线程A执行到步骤1,发现`singleton`为`null`,于是进入同步代码块,但此时线程切换到了线程B,线程B同样执行到步骤1,由于`singleton`仍为`null`,线程B也会进入同步代码块并创建一个新的实例。当线程A再次获得CPU时间片,它会跳过步骤2(因为线程B已经完成了),直接执行步骤3,这样就创建了两个`Singleton`实例,违反了单例模式的基本原则。 为了解决这个问题,Java引入了 volatile 关键字。volatile 可以禁止指令重排序,并确保所有线程看到的 `singleton` 始终是最新的。修改后的代码如下: ```java public class Singleton { private volatile static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { // 第一次检查 synchronized (Singleton.class) { if (singleton == null) { // 第二次检查 singleton = new Singleton(); // 现在是安全的 } } } return singleton; } } ``` 现在,即使有指令重排序,线程B也无法看到未完全初始化的 `singleton`,因为 `volatile` 确保了线程可见性和禁止了重排序。 然而,随着Java内存模型的进一步发展,从Java 5开始,`Singleton`的饿汉式实现也成为了一种推荐的做法。这种方式在类加载时就完成了实例化,避免了多线程同步的问题: ```java public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } } ``` 这种方式虽然看起来不是懒加载,但在Java中,类加载是线程安全的,因此这种实现方式是线程安全且高效的。 除此之外,枚举单例也是推荐的一种方式,它不仅简单,而且天然线程安全: ```java public enum Singleton { INSTANCE; public void whateverMethod() { } } ``` 这里,Singleton是一个枚举类型,只有一个INSTANCE实例,枚举在JVM中是天然单例的,不允许任何形式的子类化,因此保证了单例的唯一性。 Java中的单例模式有很多种实现方式,每种都有其适用场景和优缺点。理解这些实现方式以及它们背后的内存模型和并发原理对于编写高效、线程安全的代码至关重要。
2017-12-04 上传
2018-12-20 上传