设计模式之单例设计模式
- 单例的实现
- 懒汉式
- 线程不安全
- 线程安全
- 饿汉式
- DCL(双重校验锁)
- 登记式/静态内部类
单例设计模式:
1.保证一个类只有一个实例 (控制某些共享资源的访问权限 如 数据库或文件)
2.为该实例提供一个全局访问节点
实现步骤:
1.将默认构造方法设置为私有,防止其他对象使用单例类 new运算符
2.新建一个静态构建方法作为构造函数,该函数自动调用私有构造函数来创建对象,并将其保存在一个静态成员变量中。此后 所有对于该函数的调用都将返回这一缓存对象
懒汉式(线程不安全)
public class Singleton {
`private static Singleton instance;`
`private Singleton (){}`
`public static Singleton getInstance() {`
`if (instance == null) {`
`instance = new Singleton();`
`}`
`return instance;`
`}`
}
懒汉式(线程安全) 说真正用到它的时候才去创建实例 synchronized锁的粒度太大
public class Singleton {
`private static Singleton instance;`
`private Singleton (){}`
`public static synchronized Singleton getInstance() {`
`if (instance == null) {`
`instance = new Singleton();`
`}`
`return instance;`
`}`
三、饿汉式(程序刚启动时就创建了实例)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
四、DCL(双重校验锁)
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getInstance(){
/*
一堆业务处理代码
*/
if(null == singleton){
synchronized(Singleton.class){//锁粒度变小
if(null == singleton){//DCL
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new Singleton();
}
}
}
return singleton;
}
public static void main(String[] args) {
for (int i=0;i<1000;i++){
new Thread(()-> System.out.println(Singleton.getInstance().hashCode())).start();
}
}
}
通过在第10行又加了一层if判断,也就是所谓的Double Check Lock。也就是说即便拿到锁了,也得去作一步判断,如果这时判断对像不为空,那么就不用再创建对象,直接返回就可以了,很好的解决了“改进2”中的问题。但这里第8行是不是可以去了,我个人觉得都行,保留第8行的话,是为了提升效率,因为如果去了,每个线程过来就直接抢锁,抢锁本身就会影响效率,而if判断就几ns,且大部分线程是不需要抢锁的,所以最好保留。 到这DCL 单例的原理就介绍完了,但是还是有一个问题。就是需要考虑指令重排序的问题,因此得加入volatile来禁止指令重排序。继续分析代码,为了分析方便这里将Singleton代码简化
---
单例模式使用DCL(双重检验锁的)的原理:
单例模式 有饿汉式和懒汉式,
饿汉式在程序刚启动就创建实例,虽然线程安全,但是比较浪费资源, 懒汉式在 在需要的时候才会去创建实例,但是在多线程并发的情况下存在线程不安全的问题,
优化1:是加synchronize关键字,但是synchronize关键字直接加在放上下粒度太大。
优化2:使用 synchronize(Singleton.class){try{}catch()} 来减少锁的粒度,但是并发就会又存在问题 当线程1在准备抢锁的时候可能会被阻塞(如时间片到),这时候线程二 上cpu运行,顺利拿到锁并结束运行释放锁。线程1 恢复,拿到锁吗,运行结束。造成了的线程不安全.
优化3: 尽管某一个线程抢到了锁,也要判断当前 是否已存在实例化的对象,存在了就不在创建,不存在在去实例化。 优化4:就是需要考虑指令重排序的问题,因此得加入volatile来禁止指令重排序
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的