java 单例模式 指令重排序

小小丁灬 发布于 2016/09/05 13:42
阅读 1K+
收藏 1

   文中说:箭头所指的地方由于指令重排序问题,无法保证生成单个实例.

   我有点不明白.红框框那里,2都没执行完毕,线程1还持有锁,线程2怎么可能进同步锁?


   还有枚举方法的单例模式,

public enum SingletonEnum {  
    /** 
     * 1.从Java1.5开始支持; 
     * 2.无偿提供序列化机制; 
     * 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候; 
     */  
    instance;  
    private String others;  
    SingletonEnum() {  
  
       } 
 }



如果这个单例中还有name,age 等属性,怎么在创建之后放进去,又怎么拿出来?


加载中
0
JoeyXie
JoeyXie

第一个问题:2没有执行完毕,但是因为这时候已经把单例对象指向了新分配的地址,那么它就不是null,因此另一个线程不会进同步锁,而是在第一个null判断的时候会发现该对象已经不是null了,就直接进入return,把未初始化完成的对象返回,因为未初始化完成,所以这个线程直接使用该对象会出错。加了volatile关键字去修饰instance的话,那么另一个线程去读instance的读操作就会排在写操作之后执行,也就是可以保证得到的要么是初始化完成的对象,要么是仍然为null的对象;


第二个问题:直接在SingletonEnum里添加你想要的属性和get/set方法,如:

private String name;

private int age;

public void setName(String n){ name = n;}

public String getName() { return name;}

1
首席撸出血
首席撸出血

回答一下把



1.publicstaticSingleton getInstance()   {
 2.       if(singleton==null)  {
 3.           synchronized(Singleton.class) {
 4.               if(singleton==null)  {
 5.                   singleton=new Singleton();
 6.               }
 7.           }
 8.       }
 9.       returnsingleton;
 10.   }

这里的代码你看是没有问题

请认真的看上面的第2和第5行


再重述一遍new的过程

a.给 singleton 分配内存
b.调用 Singleton 的构造函数来初始化成员变量,形成实例
c.将singleton对象指向分配的内存空间(执行完这步 singleton才是非 null 了)




如果按a-b-c执行是没有问题的,但是如果发生了重排序,我的执行顺序为a-c-b



那么对应最上方的代码,线程1执行到第5行,做了new操作,但是发生了重排序,也就是所谓的a-c-b顺序,然后又有一个甚至多个线程(设想高并发情况),执行到了第2行,但是他的判断

2.       if(singleton==null)  {
返回的是false;


然后其他线程拿到了singleton,但是这里的singleton却没有实例化,顺理成章的就报错了


不知道我说的你理解了没


1
V
ValSong
还是感觉静态内部类单例勇者最顺手,既然都是单例了,你再弄个成员变量那么是不是设计有问题?
0
b
beastxiao
定义的时候直接初始化吧,这样不会出啥岔子
0
乌龟壳
乌龟壳

说白了,执行完instance = new Singleton()之后,可能实际的数据并未写入instance的内存。另外一个线程再判断的时候,instance还是null。

为了保证对instance的修改能立即反馈到内存中。也就是instance = new Singleton()之后,就保证了instance在内存中了,增加了volatile关键字去告知编译器实现这项功能。

0
554330833a
554330833a
什么样的代码写完会出现指令重排呢
0
一只小桃子
一只小桃子
哪里看到的这东西,没说对吧貌似
一只小桃子
一只小桃子
可以把字节码打印出来,一行行看
0
坚持的螃蟹
这是懒加载,建议在类的动态加载时创建他的静态实例
返回顶部
顶部