您的位置:首页 > 编程语言 > Java开发

Java Concurrency — 原子变量类

2015-10-25 14:05 288 查看
《Java并发编程实践》第15章 原子变量与非阻塞同步机制 读书笔记

Java中的对共享变量访问的同步机制

synchronized内部锁

显示锁

volatile

原子变量

volatile变量 VS 锁

volatile变量更轻量级,因为它们不会引起上下文的切换和线程的调度。对于锁,当频繁地发生锁的竞争时,上下文切换和调度开销可能远大于工作开销。

都提供了可见性保证。

volatile变量不能用于构建原子化的复合操作,例如i++。

加锁的缺点。当一个线程正在等待锁时,它不能做任何事情。如果另一个线程在持有锁的情况下发生延迟(原因包括页错误、调度延迟等),那么其他所有需要该锁的线程都不能前进了。

原子变量类(Atomic Variable Classes)

于是,就需要类似于volatile变量的机制,并且还要支持原子化更新的技术。原子变量类就满足了这样的需求。

原子变量类共12个,分4组

计数器

AtomicInteger AtomicLong AtomicBoolean AtomicReference


域更新器(field updater)

AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater


数组

AtomicIntegerArray AtomicLongArray AtomicReferenceArray


复合变量

AtomicMarkableReference AtomicStampedReference


Atomic类的实现依赖于冲突监测,从而能判定更新过程中是否存在来自于其他成员的干涉,在冲突发生的情况下,操作失败,并会重试(也可能不重试)。

现代的处理器提供了原子化的读-改-写指令,如比较并交换(compare-and-swap)CAS有3个操作数,内存位置V,旧的预期值A,新值B。当且仅当V等于旧的预期值A时,CAS用新值B原子化地更新V的值,否则什么都不会做。在任何一种情况下,都会返回V的真实值。

若处理器不支持这样的指令,JVM会使用自旋锁

利用AtomicInteger实现i++复合操作。

AtomicInteger i = new AtomicInteger(0);
i.incrementAndGet();


相关Java源码

package java.util.concurrent.atomic;
import sun.misc.Unsafe;

public class AtomicInteger extends Number implements java.io.Serializable {

public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}


incrementAndGet方法体内是一个for循环,表示如果冲突发生,就不断重试,直到compareAndSet方法返回true。

compareAndSet方法调用的是Unsafe类的compareAndSwapInt方法,该方法是一个native方法。

public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);


该native方法的最终实现源码路径为openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp

// Adding a lock prefix to an instruction on MP machine
// VC++ doesn't like the lock prefix to be on a single line
// so we can't insert a label after the lock prefix.
// By emitting a lock prefix, we can define a label after it.
#define LOCK_IF_MP(mp) __asm cmp mp, 0  \
__asm je L0      \
__asm _emit 0xF0 \
__asm L0:

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
}
}


如源代码所示,如果是多处理器机器,LOCK_IF_MP(mp)就会为cmpxchg指令加上lock前缀。原因是有可能多个处理器同时从各自的缓存中读取共享变量,分别进行操作,然后分别写入系统内存当中。这时就需要加锁,其中一种方式就是总线锁。

ABA问题

CAS只检测”V的值是否仍为A“,然而可能V的变化过程是A -> B -> A,这就是ABA问题。要知道“V的值在我上次观察后是否发生变化”,一种解决方案是,更新一对值,包括引用和版本号。A改为B,又改回A,版本号不同。AtomicStampedReference或AtomicMarkableReference提供了一对变量原子化的条件更新。

AtomicStampedReference更新对象引用的整数对

AtomicMarkableReference更新对象引用的布尔对
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 并发 Atomic