<b>撤废Java神话之三</b>[Java编程]
本文“<b>撤废Java神话之三</b>[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
java中原子操作是线程安全的论调常常被提到.按照定义,原子操作是不会被打断的操作,因此被认为是线程安全的.实际上有一些原子操作不一定是线程安全的.
这个问题呈现的缘由是尽大概削减在代码中同步关键字.同步会侵害性能,固然这个丧失因JVM差别而差别.别的,在现代的JVM中,同步的性能正在渐渐提高.固然如此,利用同步仍旧是有性能代价的,并且程序员永久会极力提高他们的代码的效率,因此这个问题就持续了下来.
在java中,32位大概更少位数的赋值是原子的.在一个32位的硬件平台上,除了double和long型的别的原始范例普通都是利用32位举行表示,而double和long普通利用64位表示.别的,对象引用利用本机指针实现,普通也是32位的.对这些32位的范例的操作是原子的.
这些原始范例普通利用32位大概64位表示,这又引入了另一个小小的神话:原始范例的大小是由语言保证的.这是不对的.java语言保证的是原始范例的表数范围而非JVM中的存储大小.因此,int型老是有相同的表数范围.在一个JVM上大概利用32位实现,而在另一个JVM上大概是64位的.在此再次夸大:在全部平台上被保证的是表数范围,32位以及更小的值的操作是原子的.
那么,原子操作在什么情形下不是线程安全的?主要的一点是他们大概确切是线程安全的,但是这没有被保证!java线程答应线程在自己的内存区保存变量的副本.答应线程利用本地的私有拷贝举行工作而非每次都利用主存的值是为了提高性能.考虑下面的类:
class RealTimeClock
{
private int clkID;
public int clockID()
{
return clkID;
}
public void setClockID(int id)
{
clkID = id;
}
//...
}
目前考虑RealTimeClock的一个实例以及两个线程同时调用setClockID和clockID,并发生以下的事件序列:
T1 调用setClockID(5)
T1将5放入自己的私有工作内存
T2调用setClockID(10)
T2将10放入自己的私有工作内存
T1调用clockID,它返回5
5是从T1的私有工作内存返回的
对clockI的调用应当返回10,因为这是被T2设置的,但是返回的是5,因为读写操作是对私有工作内存的而非主存.赋值操作当然是原子的,但是因为JVM答应这种行为,因此线程安全不是一定的,同时,JVM的这种行为也不是被保证的.
两个线程拥有自己的私有拷贝而不和主存一致.假如这种行为呈现,那么私有本机变量和主存一致必须在以下两个条件下:
1、变量利用volatile声明
2、被拜候的变量处于同步办法大概同步块中
假如变量被声明为volatile,在每次拜候时城市和主存一致.这个一致性是由java语言保证的,并且是原子的,即便是64位的值.(注意很多JVM没有精确的实现volatile关键字.你可以在www.javasoft.com找到更多的信息.)别的,假如变量在同步办法大概同步块中被拜候,当在办法大概块的进口处得到锁以及办法大概块退出时释放锁是变量被同步.
利用任何一种办法都可以保证ClockID返回10,也就是精确的值.变量拜候的频度差别则你的挑选的性能差别.假如你更新很多变量,那么利用volatile大概比利用同步更慢.记着,假如变量被声明为volatile,那么在每次拜候时城市和主存一致.与此对比,利用同步时,变量只在得到锁和释放锁的时刻和主存一致.但是同步使得代码有较少的并发性.
假如你更新很多变量并且不想有每次拜候都和主存举行同步的丧失大概你因为别的的缘由想解除并发性时可以考虑利用同步.
以上是“<b>撤废Java神话之三</b>[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |