java原子操作

来源:互联网 发布:歌洛依淘宝旗舰店 编辑:程序博客网 时间:2024/05/16 11:53

在有关Java线程的讨论中,一个常被提到的认识是“原子操作不需要进行同步控制”。“原

子操作”(atomic operation)即不能被线程调度机制中断的操作;一旦操作开始,那


 

 

么它一定可以在可能发生的“上下文切换”(context switch)之前(切换到其它线程执

行)执行完毕。

 

还有一个常被提到的知识是,如果问题中的变量类型是除long或double以外的基本类型,

对这种变量进行简单的赋值或者返回值操作的时候,才算是原子操作。不包括long和

double的原因是因为它们比其它基本类型要大,所以JVM不能把对它的读取或赋值当成是

单一原子操作(也许JVM能够这么做,但这并不能保证)。然而,你只要给long或double

加上volatile,操作就是原子的了。

 

如果你把原子操作的概念尝试着应用到 SynchronizedEvenGenerator.java,你将注

意到

 

public     synchronized      int getValue() {           return i; }

 

是符合这个定义的。但如果试着去掉synchronized,测试将会失败。因为尽管return i

确实是原子操作,去掉synchronized的话,将会出现当对象还处于不稳定的中间状态的

时候就被别的线程读取了。所以在试图做这样的优化之前,你必须真正知道自己在做什么。

而这并没有简单可行的规则。

 

作为第二个例子,考虑一下更简单的情况:一个产生序列号的类3。每次调用

nextSerialNumber( )          ,它必须向调用者返回一个唯一值:

 

//: c13:SerialNumberGenerator.java

 

public     class SerialNumberGenerator {

private      static       volatile       int serialNumber = 0;

public     static       int nextSerialNumber() {

return serialNumber++;

  }

///:~

 

SerialNumberGenerator基本上与你能想到的一样简单,如果你有C++或其它低级语言

的背景,你可能认为自增加操作是一个原子操作,因为它通常可以用一条微处理器指令实

现。然而,在JVM中的自增加操作并不是原子的,它牵涉到一次读和一次写,所以即使在这

样简单的操作中,也为线程出问题提供了空间。

 

serialNumber字段标记成volatile,其原因是每个线程都可能拥有一个本地栈以维护

一些变量的复本。如果把一个变量定义成volatile,就等于告诉编译器不要做任何优化,

这些优化可能会移除那些使字段与线程里的本地数据复本保持同步的读写操作。

 

要证明这一点,我们需要一个不会用完内存的集合,这时,检测到问题要花很长时间。这

里的CircularSet重用了用来存储整型数组的内存,并假设在你访问数组时,覆写值时发

生冲突的可能性最小。add( )和contains( )方法标记为synchronized以防止线程冲

突。

SerialNumberChecker含有一个静态的CircularSet,后者包含了所有已经生成的序

列号,以及一个获取序列号并能确保其唯一的嵌套线程。通过建立多个线程来争夺序列号,

你会发现线程很快就会得到重复的序列号,(注意这个程序在你的机器上可能并不冲突,

但在一台多处理器的机器上确实检查到了冲突)。要解决这个问题,就得给

nextSerialNumber( )方法加上synchronized关键字。

 

原子操作只有在对基本类型进行读取或赋值的时候才被认为是安全的。不过,正如在

EvenGenerator.java中所见,原子操作也很容易访问到对象尚出于不稳定状态时的值,

所以你不能做任何假设。不仅如此,原子操作也不保证对long和double类型能工作(尽

管有些JVM实现确实能保证对long和double类型操作的原子性,但如果你依赖于这一点,

你的代码就失去了可移植性)。

 

最安全的就是使用以下方针:

 

1。如果你要对类中的某个方法进行同步控制,最好同步所有方法。如果你忽略

了其中一个,通常很难确定这么做是否会有负面影响。

 

2。当去除方法的同步控制时,要非常小心。通常这么做是基于性能方面的考虑,

但在JDK1.3  和JDK1.4 中,同步控制所需的负担已经大大减少。此外,你只应

该在使用了性能评价工具证实了同步控制确实是性能瓶颈的时候,才能这么做。

 


原创粉丝点击