网站首页 > 基础教程 正文
老实说,双重检查锁在Java界,用的人多,出锅的也多。
为什么要用双重检查?因为我们想偷懒!对,就是偷懒。同步开销大,所以想让同步只在必要的时候触发,也就是“实例还没创建”的时候。但这事儿在早年的Java版本里其实坑不少。
先看代码——经典的双重检查写法,大家面试背得都很熟:
public class Singleton {
privatevolatilestatic Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
注意关键点:volatile,这个必须得有。要是没有,那你这套双检锁就是废的。
为啥这么说?因为JVM在new对象的时候,其实做了不少“花活”。你以为一句instance = new Singleton();就是一步操作?太天真了,其实它可以被拆成三步:
- 分配内存
- 调用构造函数
- 将内存地址赋给instance
而JVM可能因为指令重排序,先把引用赋了,再去初始化对象。你说坑不坑?于是某个线程拿到的instance就可能是个“半成品”,这就跟你早上去食堂打饭,阿姨给你一个还没熟的鸡腿,说:“这熟了!”你敢吃?
从JDK 1.5开始,volatile语义被加强,保证了写入操作的“happens-before”关系,这才让这招变得安全可靠。
但是话说回来,这写法我真不推荐在线上系统里用。为什么?太容易出错了,尤其你团队里有实习生的时候……
我一般怎么做呢?直接上静态内部类:
public class Singleton {
private Singleton() {}
privatestaticclass Holder {
privatestaticfinal Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
这方式的优点显而易见:
- JVM保证线程安全
- 懒加载(只有在第一次调用getInstance()时才初始化)
- 没有同步锁的性能开销
- 写起来还贼清爽,不容易错
你说是不是香?
当然,也不是所有人都喜欢这套,尤其有些人看着内部类就犯密集恐惧症,那你还可以考虑用枚举实现单例:
public enum Singleton {
INSTANCE;
public void doSomething() {
// 方法逻辑
}
}
Java里enum天然就是单例的,反序列化和反射攻击都拦得住(除非你有邪门招数),简直像开了挂。但这方式我一般只在“真·单例”的场景下用,比如注册中心、线程池管理器这种,毕竟枚举不太灵活,扩展性差点意思。
那什么时候该用双重检查锁?只在你明确知道这对象构造非常昂贵,且懒加载带来显著收益的场景下。
比如搞个缓存系统,初始化要跑去数据库load一堆配置,这时候懒加载是有意义的。否则就别折腾这套——直接静态初始化或者内部类解决,开发效率和安全性都更高。
现在有些人连写个线程安全单例都喜欢整成Spring Bean……
“反正Spring都能管,你搞个@Component再配个@Scope("singleton")不就完事了?”
这不是不行,而是你把简单问题复杂化了。单例本来JVM自己就能搞定,非得整出IOC来托管,是不是有点吃饱了撑的嫌疑?
所以结论很简单:技术是为业务服务的,别为了“耍帅”搞些炫技式的设计,到头来团队背锅你背大头,这事我见得多了……
猜你喜欢
- 2025-06-18 单例模式谁都会,破坏单例模式听说过吗?
- 2025-06-18 Objective-c单例模式的正确写法「藏」
- 2025-06-18 单例模式介绍(单例模式都有哪些)
- 2025-06-18 前端设计-单例模式在实战中的应用技巧
- 2025-06-18 PHP之单例模式(php单例模式连接数据库)
- 2025-06-18 设计模式:单例模式及C及C++实现示例
- 2025-06-18 python的单例模式(单例 python)
- 2025-06-18 你认为最简单的单例模式,东西还挺多
- 2025-06-18 Python入门题037:实现单例类(3种方法)
- 2025-06-18 用好单例设计模式,也可以让你的代码性能提升300%
- 06-18单例模式谁都会,破坏单例模式听说过吗?
- 06-18Objective-c单例模式的正确写法「藏」
- 06-18单例模式介绍(单例模式都有哪些)
- 06-18前端设计-单例模式在实战中的应用技巧
- 06-18PHP之单例模式(php单例模式连接数据库)
- 06-18设计模式:单例模式及C及C++实现示例
- 06-18python的单例模式(单例 python)
- 06-18你认为最简单的单例模式,东西还挺多
- 最近发表
- 标签列表
-
- jsp (69)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (86)
- location.href (69)
- dockerexec (65)
- tail-f (79)
- queryselectorall (63)
- location.search (79)
- bootstrap教程 (74)
- 单例 (62)
- linuxgzip (68)
- 字符串连接 (73)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)