Atomic 从 JDK5 开始, java.util.concurrent 包里提供了很多面向并发编程的类. 使用这些类在多核 CPU 的机器上会有比较好的性能.
主要原因是这些类里面大多使用 (失败 - 重试方式的) 乐观锁而不是 synchronized 方式的悲观锁.
跟踪了一下 AtomicInteger 的 incrementAndGet 的实现仅做个笔记, 方便以后再深入研究
1. incrementAndGet 的实现
- public final int incrementAndGet() {
- for (;;) {
- int current = get();
- int next = current + 1;
- if (compareAndSet(current, next))
- return next;
- }
- }
首先可以看到他是通过一个无限循环 (spin) 直到 increment 成功为止.
循环的内容是
1. 取得当前值
2. 计算 + 1 后的值
3. 如果当前值还有效 (没有被) 的话设置那个 + 1 后的值
4. 如果设置没成功(当前值已经无效了即被别的线程改过了), 再从 1 开始.
2. compareAndSet 的实现
- public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
- }
直接调用的是 UnSafe 这个类的 compareAndSwapInt 方法
全称是 sun.misc.Unsafe. 这个类是 Oracle(Sun)提供的实现. 可以在别的公司的 JDK 里就不是这个类了
3. compareAndSwapInt 的实现
- /**
- * Atomically update Java variable to <tt>x</tt> if it is currently
- * holding <tt>expected</tt>.
- * @return <tt>true</tt> if successful
- */
- public final native boolean compareAndSwapInt(Object o, long offset,
- int expected,
- int x);
可以看到, 不是用 Java 实现的, 而是通过 JNI 调用操作系统的原生程序.
4. compareAndSwapInt 的 native 实现
如果你下载了 OpenJDK 的源代码的话在 hotspot\src\share\vm\prims \ 目录下可以找到 unsafe.cpp
- UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
- UnsafeWrapper("Unsafe_CompareAndSwapInt");
- oop p = JNIHandles::resolve(obj);
- jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
- return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
- UNSAFE_END
可以看到实际上调用 Atomic 类的 cmpxchg 方法.
5. Atomic 的 cmpxchg
这个类的实现是跟操作系统有关, 跟 CPU 架构也有关, 如果是 windows 下 x86 的架构
实现在 hotspot\src\os_cpu\windows_x86\vm \ 目录的 atomic_windows_x86.inline.hpp 文件里
- inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
- // alternative for InterlockedCompareExchange
- int mp = os::is_MP();
- __asm {
- mov edx, dest
- mov ecx, exchange_value
- mov eax, compare_value
- LOCK_IF_MP(mp)
- cmpxchg dword ptr [edx], ecx
- }
- }
在这里可以看到是用嵌入的汇编实现的, 关键 CPU 指令是 cmpxchg
到这里没法再往下找代码了. 也就是说 CAS 的原子性实际上是 CPU 实现的. 其实在这一点上还是有排他锁的. 只是比起用 synchronized, 这里的排他时间要短的多. 所以在多线程情况下性能会比较好.
代码里有个 alternative for InterlockedCompareExchange
这个 InterlockedCompareExchange 是 WINAPI 里的一个函数, 做的事情和上面这段汇编是一样的
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683560(v=vs.85).aspx
6. 最后再贴一下 x86 的 cmpxchg 指定
- Opcode CMPXCHG CPU: I486 + Type of Instruction: User Instruction: CMPXCHG dest,
- src Description: Compares the accumulator with dest.If equal the "dest"is loaded with "src",
- otherwise the accumulator is loaded with "dest".Flags Affected: AF,
- CF,
- OF,
- PF,
- SF,
- ZF CPU mode: RM,
- PM,
- VM,
- SMM+++++++++++++++++++++++Clocks: CMPXCHG reg,
- reg 6 CMPXCHG mem,
- reg 7(10
- if compartion fails)
来源: http://www.bubuko.com/infodetail-2510899.html