设计模式【1.3】-- 为什么饿汉式单例是线程安全的?
我们都知道,饿汉式单例是线程安全的,也就是不会初始化的时候创建出两个对象来,但是为什么呢?
首先定义一个饿汉式单例如下:
public class Singleton {
// 私有化构造方法,以防止外界使用该构造方法创建新的实例
private Singleton(){
}
// 默认是public,访问可以直接通过Singleton.instance来访问
static Singleton instance = new Singleton();
}
之所以是线程安全的,是因为JVM在类加载的过程,保证了不会初始化多个static
对象。类的生命周期主要是:
加载-->验证-->准备-->解析-->初始化-->使用-->卸载
上面的代码,实际上类成员变量instance
是在初始化阶段的时候完成初始化,所有的类变量以及static
静态代码块,都是在一个叫clinit()
的方法里面完成初始化。这一点,使用jclasslib
可以看出来:
clinit()
方法是由虚拟机收集的,包含了static
变量的赋值操作以及static
代码块,所以我们代码中的static Singleton instance = new Singleton();
就是在其中。虚拟机本身会保证clinit()
代码在多线程并发的时候,只会有一个线程可以访问到,其他的线程都需要等待,并且等到执行的线程结束后才可以接着执行,但是它们不会再进入clinit()
方法,所以是线程安全的。我们可以验证一下:
首先改造一下单例:
public class Singleton {
// 私有化构造方法,以防止外界使用该构造方法创建新的实例
private Singleton() {
}
// 默认是public,访问可以直接通过Singleton.instance来访问
static Singleton instance = null;
static {
System.out.println("初始化static模块---开始");
instance = new Singleton();
try {
System.out.println("初始化中...");
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("初始化static模块----结束");
}
}
测试代码:
import java.io.*;
import java.lang.reflect.InvocationTargetException;
public class SingletonTests {
public static void main(String[] args) throws Exception, InvocationTargetException, InstantiationException, NoSuchMethodException {
Thread thread1 = new Thread(new Runnable() {
public void run() {
System.out.println("线程1开始尝试初始化单例");
Singleton singleton = Singleton.instance;
System.out.println("线程1获取到的单例:" + singleton);
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
System.out.println("线程2开始尝试初始化单例");
Singleton singleton = Singleton.instance;
System.out.println("线程2获取到的单例:" + singleton);
}
});
thread1.start();
thread2.start();
}
}
运行结果,一开始运行的时候,我们可以看到线程1进去了static
代码块,它在初始化,线程2则在等待。
待到线程1初始化完成的时候,线程2也不会再进入static
代码块,而是和线程1取得同一个对象,由此可见,static
代码块实际上就是线程安全的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库