提到java原子类,不得不说下关键字volatile和内存可见性。内存可见性问题,可以看下《java并发编程实践》讲的很详细。下面的示例代码是错误的,因为没有使用volatile来保证内存的可见性,所以如果1个线程修改了共享变量,那么另外一个线程可能永远也看不到修改后的值。如果将stop修改成volatile类型的,就可以保证一个线程对volatile变量的修改,对另一个线程是可见的。
public class Test { private static boolean stop = false; // 以-server模式启动JVM public static void main(String[] args) throws Exception { new Thread(new Runnable() { public void run() { while (!stop) { } System.out.println("over"); } }).start(); Thread.sleep(200); stop = true; } }
volatile和可见性,可以看下“深入理解Java内存模型(四)——volatile”这篇文章讲解的很浅显易懂。
了解了volatile和可见性之后,我们回到主题看下set和lazySet的差别,jdk源码的javadoc描述的相当简单:
/** * Sets to the given value. * * @param newValue the new value */ public final void set(int newValue) { value = newValue; } /** * Eventually sets to the given value. * * @param newValue the new value * @since 1.6 */ public final void lazySet(int newValue) { unsafe.putOrderedInt(this, valueOffset, newValue); }
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7065550
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6275329
As probably the last little JSR166 follow-up for Mustang, we added a "lazySet" method to the Atomic classes (AtomicInteger, AtomicReference, etc). This is a niche method that is sometimes useful when fine-tuning code using non-blocking data structures. The semantics are that the write is guaranteed not to be re-ordered with any previous write, but may be reordered with subsequent operations (or equivalently, might not be visible to other threads) until some other volatile write or synchronizing action occurs). The main use case is for nulling out fields of nodes in non-blocking data structures solely for the sake of avoiding long-term garbage retention; it applies when it is harmless if other threads see non-null values for a while, but you'd like to ensure that structures are eventually GCable. In such cases, you can get better performance by avoiding the costs of the null volatile-write. There are a few other use cases along these lines for non-reference-based atomics as well, so the method is supported across all of the AtomicX classes. For people who like to think of these operations in terms of machine-level barriers on common multiprocessors, lazySet provides a preceeding store-store barrier (which is either a no-op or very cheap on current platforms), but no store-load barrier (which is usually the expensive part of a volatile-write).
set()和volatile具有一样的效果(能够保证内存可见性,能够避免指令重排序),但是使用lazySet不能保证其他线程能立刻看到修改后的值(有可能发生指令重排序)。简单点理解:lazySet比set()具有性能优势,但是使用场景很有限。在网上没有找到lazySet和set的性能数据对比,而且CPU的速度很快的,应用的瓶颈往往不在CPU,而是在IO、网络、数据库等。对于并发程序要优先保证正确性,然后出现性能瓶颈的时候再去解决。因为定位并发导致的问题,往往要比定位性能问题困难很多。
在stackoverflow上,也有很人提问set和lazySet的问题:
http://stackoverflow.com/questions/1468007/atomicinteger-lazyset-vs-set
http://stackoverflow.com/questions/25840733/atomic-integer-lazyset-performance-gains
http://stackoverflow.com/questions/7557156/atomicxxx-lazyset-in-terms-of-happens-before-edges
本人不懂硬件和CPU,对于并发编程中很多问题,只能简单的知道,不能真正的理解。先记下来这些遇到的知识点,留作以后慢慢领悟!