关于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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树