Java 线程 (十):CAS
public?final?int?incrementAndGet()?{??
????for?(;;)?{??
????????int?current?=?get();??
????????int?next?=?current?+?1;??
????????if?(compareAndSet(current,?next))??
????????????return?next;??
????}??
}??
[java]? view plain copy print ?
public?final?int?decrementAndGet()?{??
????for?(;;)?{??
????????int?current?=?get();??
????????int?next?=?current?-?1;??
????????if?(compareAndSet(current,?next))??
????????????return?next;??
????}??
}??
以这两个方法为例,incrementAndGet 方法相当于原子性的++i,decrementAndGet 方法相当于原子性的--i(根据第一章和第二章我们知道++i 或--i 不是一个原子性的操作),这两个方法中都没有使用阻塞式的方式来保证原子性(如 Synchronized),那它们是如何保证原子性的呢,下面引出 CAS。
==============================================================================
CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。简单介绍一下这个指令的操作过程:首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我原来的值是多少”。(这段描述引自《Java 并发编程实践》)
简单的来说,CAS 有 3 个操作数,内存值 V,旧的预期值 A,要修改的新值 B。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则返回 V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而 Synchronized 是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。下面来看一下 AtomicInteger 是如何利用 CAS 实现原子性操作的。
[java]? view plain copy print ?
private?volatile?int?value;??
首先声明了一个 volatile 变量 value,在 第二章我们知道 volatile 保证了变量的内存可见性,也就是所有工作线程中同一时刻都可以得到一致的值。
[java]? view plain copy print ?
public?final?int?get()?{??
????return?value;??
}??
[java]? view plain copy print ?
//?setup?to?use?Unsafe.compare
AndSwapInt?for?updates??
private?static?final?Unsafe?unsafe?=?Unsafe.getUnsafe();??
private?static?final?long?valueOffset;//?注意是静态的??
static?{??
??try?{??
????valueOffset?=?unsafe.objectFieldOffset??
????????(AtomicInteger.class.getDeclaredField("value"));//?反射出 value 属性,获取其在内存中的位置??
??}?catch?(Exception?ex)?{?throw?new?Error(ex);?}??
}??
public?final?boolean?compareAndSet(int?expect,?int?update)?{??
??return?unsafe.compareAndSwapInt(this,?valueOffset,?expect,?update);??
}??
评论