单例模式线程不安全解释

独行侠_阿涛 2018-01-19 10:39:22
public static Singleton3 getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}

这样写会导致线程问题了,比如A线程进入synchronized代码块了,执行完了instance = new Singleton()后退出代码块,但是此时还没有真正初始化,这是线程B进来了,发现instance不为null,于是就立马返回该instance(其实是没有初始化好的),然后B就开始使用该instance,却发现没初始化,于是就出问题了。
自己用伪代码方式理解了new的过程,如下:
* new一个对象可形象的表示为如下三行伪代码
* memory = allocate(); //1:分配对象的内存空间
* ctorInstance(memory); //2:初始化对象
* instance = memory; //3:使instance指向刚分配的内存地址
但是理解不了为啥这样会导致线程问题,请大神解释
...全文
1121 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
千早一 2019-10-17
  • 打赏
  • 举报
回复
引用 10 楼 maradona1984 的回复:
[quote=引用 9 楼 穿空透暗始终明 的回复:] synchronized(instance)中的 instance为null会出运行时异常吧,synchronized(instance)中的instance能为null么?
应该不会空指针,但的确有问题,一般是弄个class什么的最安全,变量变化了一样有bug吧[/quote] 刚才我试了一下,像楼主这样写,报空指针异常了,如果这样写:synchronized(Singleton3.class),这样就不会报异常了,但是可能会导致指令重排。
正怒月神 2019-10-15
  • 打赏
  • 举报
回复
我见过的8中单例模式,也只是性能上有些区别。 但是像楼主写的这个,以前非常常用。貌似并没有什么毛病。
正怒月神 2019-10-15
  • 打赏
  • 举报
回复
。。。。什么叫A没有真正初始化。。 如果你要初始化,具有参数,那就修改为有参构造函数,就好了。。。 你是一个static,A没有初始化完,谁都用不了,只能在synchronized (instance)等。
maradona1984 2019-10-15
  • 打赏
  • 举报
回复
引用 9 楼 穿空透暗始终明 的回复:
synchronized(instance)中的 instance为null会出运行时异常吧,synchronized(instance)中的instance能为null么?
应该不会空指针,但的确有问题,一般是弄个class什么的最安全,变量变化了一样有bug吧
千早一 2019-10-15
  • 打赏
  • 举报
回复
synchronized(instance)中的 instance为null会出运行时异常吧,synchronized(instance)中的instance能为null么?
Braska 2018-02-05
  • 打赏
  • 举报
回复
这里有篇资料,有提及到双重检测单例模式的线程不安全原因。单例模式
Braska 2018-02-05
  • 打赏
  • 举报
回复
引用 6 楼 Mycifeng 的回复:
synchronized (instance) { if (instance == null) { instance = new Singleton3(); } } 锁里都判断了,为啥外面还要判断,这不是给自己找事吗?
外层不加判断的话, 每个调用getInstance()方法的线程都会在这里发生阻塞。 即使instance对象已经被创建了。
Mycifeng 2018-02-05
  • 打赏
  • 举报
回复
synchronized (instance) { if (instance == null) { instance = new Singleton3(); } } 锁里都判断了,为啥外面还要判断,这不是给自己找事吗?
maradona1984 2018-02-05
  • 打赏
  • 举报
回复
引用 4 楼 hxm_Code 的回复:
[quote=引用 1 楼 oyljerry的回复:]说白了就是new不是一个原子操作,而且可能进行指令重排,并不一定是1,2,3顺序,可能是1,3,2。这样可能另一个线程在2的之前进入了。这样它直接return instance返回给其他函数使用,这个时候就会出错。所以要用volatile修饰
volatile不能用于非原子操作的[/quote] 用于这个场景是没问题的
H阿布 2018-02-04
  • 打赏
  • 举报
回复
引用 1 楼 oyljerry的回复:
说白了就是new不是一个原子操作,而且可能进行指令重排,并不一定是1,2,3顺序,可能是1,3,2。这样可能另一个线程在2的之前进入了。这样它直接return instance返回给其他函数使用,这个时候就会出错。所以要用volatile修饰
volatile不能用于非原子操作的
lsongiu86 2018-01-20
  • 打赏
  • 举报
回复
synchronized(当前class)
nwpulei 2018-01-19
  • 打赏
  • 举报
回复
之前看过 觉得不错 Java 单例真的写对了么? http://www.importnew.com/18835.html
oyljerry 2018-01-19
  • 打赏
  • 举报
回复
说白了就是new不是一个原子操作,而且可能进行指令重排,并不一定是1,2,3顺序,可能是1,3,2。这样可能另一个线程在2的之前进入了。这样它直接return instance返回给其他函数使用,这个时候就会出错。所以要用volatile修饰

50,523

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧