黑马程序员技术交流社区

标题: 深入理解Java内存模型——锁(四) [打印本页]

作者: 李江    时间: 2013-10-8 08:23
标题: 深入理解Java内存模型——锁(四)

下面我们来分析在常见的intel x86处理器中,CAS是如何同时具有volatile读和volatile写的内存语义的。

下面是sun.misc.Unsafe类的compareAndSwapInt()方法的源代码:

  1. public final native boolean compareAndSwapInt(Object o, long offset,
  2.                                               int expected,
  3.                                               int x);
复制代码
可以看到这是个本地方法调用。这个本地方法在openjdk中依次调用的c++代码为:unsafe.cpp,atomic.cpp和atomicwindowsx86.inline.hpp。这个本地方法的最终实现在openjdk的如下位置:openjdk-7-fcs-src-b147-27jun2011\openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.inline.hpp(对应于windows操作系统,X86处理器)。下面是对应于intel x86处理器的源代码的片段:
  1. // Adding a lock prefix to an instruction on MP machine
  2. // VC++ doesn't like the lock prefix to be on a single line
  3. // so we can't insert a label after the lock prefix.
  4. // By emitting a lock prefix, we can define a label after it.
  5. #define LOCK_IF_MP(mp) __asm cmp mp, 0  \
  6.                        __asm je L0      \
  7.                        __asm _emit 0xF0 \
  8.                        __asm L0:

  9. inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  10.   // alternative for InterlockedCompareExchange
  11.   int mp = os::is_MP();
  12.   __asm {
  13.     mov edx, dest
  14.     mov ecx, exchange_value
  15.     mov eax, compare_value
  16.     LOCK_IF_MP(mp)
  17.     cmpxchg dword ptr [edx], ecx
  18.   }
  19. }
复制代码

如上面源代码所示,程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(lock cmpxchg)。反之,如果程序是在单处理器上运行,就省略lock前缀(单处理器自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。

intel的手册对lock前缀的说明如下:

上面的第2点和第3点所具有的内存屏障效果,足以同时实现volatile读和volatile写的内存语义。

经过上面的这些分析,现在我们终于能明白为什么JDK文档说CAS同时具有volatile读和volatile写的内存语义了。

现在对公平锁和非公平锁的内存语义做个总结:

从本文对ReentrantLock的分析可以看出,锁释放-获取的内存语义的实现至少有下面两种方式:

concurrent包的实现

由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式:

Java的CAS会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读-改-写操作,这是在多处理器中实现同步的关键(从本质上来说,能够支持原子性读-改-写指令的计算机器,是顺序计算图灵机的异步等价机器,因此任何现代的多处理器都会去支持某种能对内存执行原子性读-改-写操作的原子指令)。同时,volatile变量的读/写和CAS可以实现线程之间的通信。把这些特性整合在一起,就形成了整个concurrent包得以实现的基石。如果我们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式:

AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类来实现的。从整体来看,concurrent包的实现示意图如下(图五):



图五.jpg (50.69 KB, 下载次数: 13)

图五

图五





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2