1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public class SingletonTest { /** * 使用volatile防止指令重排序的问题(singletonTest = new SingletonTest();) */ private volatile static SingletonTest singletonTest; private SingletonTest(){ } /** * synchronized双重锁机制 */ public static SingletonTest getInstance(){ if(singletonTest == null){ synchronized (SingletonTest.class){ if(singletonTest == null){ /* * new操作非原子操作,分为3步: * 1.为singletonTest分配分配内存 * 2.调用SingletonTest的构造函数初始化成员变量 * 3.将singletonTest指向分配的内存空间(执行完此步骤singletonTest对象就非null了) * 若不加volatile关键字,2、3两步执行顺序不确定。按照1-3-2的顺序执行则可能会return未初始化的对象 */ singletonTest = new SingletonTest(); } } } return singletonTest; } /** * 测试函数 */ public void doSth(){} /** * 启动1000个线程测试 */ public static void main(String[] args) { Thread[] threads = new Thread[10000]; for(int i = 0;i<threads.length;i++){ threads[i] = new Thread(new Runnable(){ <a href="http://www.jobbole.com/members/wx610506454">@Override</a> public void run() { SingletonTest instance = SingletonTest.getInstance(); instance.doSth(); } }); } for(int i = 0;i<threads.length;i++){ threads[i].start(); } } } |
上述代码是使用”synchronized双重锁机制”的单例模式,我一直有疑问为何要加volatile关键字?
目前得到的答案如同注释所写,是利用了volatile关键字的防止指令重排序功能,保证非原子操作的禁止重排序。
我的问题:
1.那么如何验证不加volatile抛出异常的场景(目前我的测试都是正常的!)?
2.synchronize不是也具有防止指令重排序的功能,加volatile关键字是不是多余?
最新评论
问题2,Happen-before仅仅当另一个线程获得了锁之后才成立,所以 synchronized block 里面的 if test 永远不会读到未同步好的中间值。但在当前线程synchronized初始化块退出之后,另一个线程获得锁之前,第一个在synchronized外面的if test就有可能读到未同步好的值。所以volatile还是必需的。
问题1,这个测试比较难做,还要看看生成的Byte code到底是什么顺序。但你的测试代码首先不对,这种错误仅可能发生在一个线程synchronized退出后另一个进入之前,而一旦instance不为null后,synchronized block就再也不会进入了,所以你测试getInstance一亿次也没用
赞