前言
在Java併發包中有這樣一個包,java.util.concurrent.atomic,該包是對Java部分數據類型的原子封裝,在原有數據類型的基礎上,提供了原子性的操作方法,保證了線程安全。下面以AtomicInteger爲例,來看一下是如何實現的。
- public final int incrementAndGet() {
- for (;;) {
- int current = get();
- int next = current + 1;
- if (compareAndSet(current, next))
- return next;
- }
- }
- 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。
Compare And Swap
CAS 指的是現代 CPU 廣泛支持的一種對內存中的共享數據進行操作的一種特殊指令。這個指令會對內存中的共享數據做原子的讀寫操作。簡單介紹一下這個指令的操作過程:首先,CPU 會將內存中將要被更改的數據與期望的值做比較。然後,當這兩個值相等時,CPU 纔會將內存中的數值替換爲新的值。否則便不做操作。最後,CPU 會將舊的數值返回。這一系列的操作是原子的。它們雖然看似複雜,但卻是 Java 5
併發機制優於原有鎖機制的根本。簡單來說,CAS 的含義是“我認爲原有的值應該是什麼,如果是,則將原有的值更新爲新值,否則不做修改,並告訴我原來的值是多少”。(這段描述引自《Java併發編程實踐》)
簡單的來說,CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改爲B,否則返回V。這是一種樂觀鎖的思路,它相信在它修改之前,沒有其它線程去修改它;而Synchronized是一種悲觀鎖,它認爲在它修改之前,一定會有其它線程去修改它,悲觀鎖效率很低。下面來看一下AtomicInteger是如何利用CAS實現原子性操作的。
volatile變量
- private volatile int value;
- public final int get() {
- return value;
- }
Compare And Set
- // setup to use Unsafe.compareAndSwapInt 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);
- }
循環設置
- public final int incrementAndGet() {
- for (;;) {// 這樣優於while(true)
- int current = get();// 獲取當前值
- int next = current + 1;// 設置更新值
- if (compareAndSet(current, next))
- return next;
- }
- }