前言
??博客主页: ??【】??
??精品Java专栏【Javase】、【Java数据结构】、【备战蓝桥】【JavaEE初阶】
??欢迎点赞 ?? 收藏 留言评论 ??私信必回哟????本文由 【】 原创,首发于 优快云??
??博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
??博客中涉及源码及博主日常练习代码均已上传码云(gitee)
??内容导读??
??Java多线程案例之单例模式
??1. 单例模式概述
啥是设计模式
设计模式好比象棋中的 “棋谱”. 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有
一些固定的套路. 按照套路来走局势就不会吃亏.
软件开发中也有很多常见的 “问题场景”. 针对这些问题场景, 大佬们总结出了一些固定的套路. 按照
这个套路来实现代码, 也不会吃亏.
单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例.
这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个.
单例模式具体的实现方式, 分成 “饿汉” 和 “懒汉” 两种.
- 饿汉模式:在类加载阶段就把实例创建出来
- 懒汉模式:通过getInstance方法来获取到实例,首次调用该方法的时候,才真正创建实例。
??2.单例模式实现
2.1 饿汉模式
在类加载阶段就把实例创建出来
static class Singleton {
//把构造方法设为private,防止在类外面调用构造方法,也就禁止了调用者在其他地方创建实例的机会
private Singleton() {
}
private static Singleton instance=new Singleton();
public static Singleton getInstance() {
return instance;
}
}
如果多个线程调用getInstance,是否出现问题呢?
此处认为是线程安全的,因为是多个线程读(只有return instance),不涉及修改。
2.2 懒汉模式-单线程版
类加载的时候不创建实例. 第一次使用的时候才创建实例
static class Singleton {
private Singleton() {}
private static Singleton instance=null;
public static Singleton getInstance() {
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
2.3 懒汉模式-多线程版
在上述2.2的代码示例中,如果实例已经创建完毕,后续再调用getInstance,此时不涉及修改操作,是线程安全的。
但是如果实例尚未创建,此时就可能会涉及到修改(分为两步 LOAD和SAVE),如果确实存在多个线程同时修改,就会涉及到线程安全问题。
如何解决这里的问题呢?
加锁
注意!!!
这是一种典型的错误写法,此处线程不安全主要是因为if操作和=操作不是原子的,我们加锁的目的是让代码具有原子性,就应该使用synchronized把这两个操作包裹上。
注意!!!
如果这样加锁,确实解决了线程安全问题,但是也引入了新问题,getInstance是首次调用的时候才涉及线程安全问题,在后续调用时就不涉及了(后续不存在修改操作了),所以如果我们按照刚才这个加锁方法,不仅仅时首次调用,包括后续的调用也会涉及到加锁操作
后续本来没有线程安全问题,不需要加锁,如果加锁便是多此一举,
加锁操作本身是一个开销比较大的操作,在不该加锁的地方加锁了,可能会让这个代码的速度降低很多倍。
2.4 懒汉模式-多线程版(改进)
为了解决2.3中提出的问题,我们想出了下边这种解决方式
首次调用的时候就加锁,后续调用的时候就不加锁,改进后的代码我们发现,if(instance==null)这一个条件在synchronized内外各出现了一次,如果没有synchronized这样的代码是无意义的,但是有了synchronized之后,这个代码及就很关键,看似这两条判读语句紧挨着,但是由于有锁的干预,就会使得这两条代码相距很远。
最后的话
总结不易,希望uu们不要吝啬你们的??哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正??