您的位置:首页 > 编程语言 > Java开发

关于synchronized具备原子性的问题

2015-08-08 18:34 190 查看

关于synchronized具备原子性的问题

1 原子性的定义:

原子操作(atomic operation)是不需要synchronized,这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

2 深入理解Java虚拟机对synchronized的描述:

java内存模型提供了lock和unlock操作来满足更大范围的需求,尽管虚拟机没有直接lock和unlock开放给用户使用,但却提供了更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作,这两个字节码反映到Java代码中就是同步块synchronized关键字,因此synchronized块之间额操作也具有原子性。

3 我的问题,如果说程序中有一个特别耗时的synchronized块,在单cpu的环境下运行,因其原子性,岂不会阻塞其他程序的运行,这肯定是不允许的?

4 搜索资料

Double Check Lock Singleton:下面是一个dcl单例模式的代码段,举这个例子一方面解释synchronized块是否可中断性,一方面解释dcl singleton是否具有多线程安全性。

class Singleton {
private static Singleton instance = null;

public static Singleton instance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();//step1
}
}
return instance;
}
}


为了方便说明,通过分析对象实例化的汇编代码,来确定dcl singleton是否具有多线程安全,而其不安全正是因为在synchronized块不恰当的中断导致的,也就解释了上述的疑问。

设一行Java代码:

Objects[i].reference = new Object();


经过Symantec JIT编译器编译过以后,最终会变成如下汇编码在机器中执行:

[pre]
0206106A  mov     eax,0F97E78h
0206106F  call      01F6B210             ;为Object申请内存空间
; 返回值放在eax中

02061074  mov     dword ptr [ebp],eax    ; EBP 中是objects[i].reference的地址
; 将返回的空间地址放入其中
; 此时Object尚未初始化

02061077  mov     ecx,dword ptr [eax]    ; dereference eax所指向的内容
; 获得新创建对象的起始地址

02061079  mov     dword ptr [ecx],100h   ; 下面4行是内联的构造函数
0206107F  mov     dword ptr [ecx+4],200h
02061086  mov     dword ptr [ecx+8],400h
0206108D  mov     dword ptr [ecx+0Ch],0F84030h
[/pre]


可见,Object构造函数尚未调用,但是已经能够通过objects[i].reference获得Object对象实例的引用。

如果把DCL单例代码放到多线程环境下运行,某线程在执行到step1代码的时候JVM或者操作系统进行了一次线程切换,其他线程显然会发现instance 对象已经不为空,导致Lazy load的判断语句if(instance == null)不成立。线程认为对象已经建立成功,随之可能会使用对象的成员变量或者调用该对象实例的方法,最终导致不可预测的错误。

5 我的结论

synchronized块不同于真正意义的原子性操作,执行时是可以中断的,通过锁实现了线程访问共享数据的串行化。DCL单例模式在多线程的环境下是不安全的,若线程t1恰好在instance引用获得内存地址,但是对象尚未构造完成时被中断,线程t2发现instance引用已经!=null,直接调用instance的方法和属性,将导致意想不到的错误(对象锁仍然属于线程t1)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 多线程 线程