java並發-原子性


原子性就是指該操作是不可再分的。
java.util.concurrent.atomic中有一組使用無鎖算法實現的原子操作類。AtomicInteger、AtomicBoolean、AtomicLong 外還有
AtomicReference 。它們分別封裝了對整數、整數數組、長整型、長整型數組和普通對象的多線程安全操作。
這些都是居於CAS算法實現的。CAS即:Compare and Swap,是比較並交換的意思。

CAS 簡介

CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。

非阻塞算法 (nonblocking algorithms)
一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的算法。
現代的CPU提供了特殊的指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。
拿出AtomicInteger來研究在沒有鎖的情況下是如何做到數據正確性的。

private volatile int value;

首先毫無以為,在沒有鎖的機制下可能需要借助volatile原語,保證線程間的數據是可見的(共享的)。

這樣才獲取變量的值的時候才能直接讀取。

public final int get() { return value; }

然后來看看++i是怎么做到的。

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

在這里采用了CAS操作,每次從內存中讀取數據然后將此數據和+1后的結果進行CAS操作,如果成功就返回結果,否則重試直到成功為止。而compareAndSet利用JNI來完成CPU指令的操作。

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

整體的過程就是這樣子的,利用CPU的CAS指令,同時借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的。

CAS缺點

  1. ABA問題。因為CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那么使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那么A-B-A 就會變成1A-2B-3A。

從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標志是否等於預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。

關於ABA問題參考文檔: http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html

  1. 循環時間長開銷大。自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。如果JVM能支持處理器提供的pause指令那么效率會有一定的提升,pause指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間取決於具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執行效率。

  2. 只能保證一個共享變量的原子操作。當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖,或者有一個取巧的辦法,就是把多個共享變量合並成一個共享變量來操作。比如有兩個共享變量i=2,j=a,合並一下ij=2a,然后用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行CAS操作。
    這里寫圖片描述

    應用實例

    1:AtomicInteger 使用
class Counter { private volatile int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } } class Counter { private AtomicInteger count = new AtomicInteger(); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }

2:實現AtomicFloat(原文地址

import java.util.concurrent.atomic.AtomicInteger; /** * 因為Java沒有提供AtomicFloat * 所以自己實現一個 * @author 楊尚川 */ public class AtomicFloat extends Number { private AtomicInteger bits; public AtomicFloat() { this(0f); } public AtomicFloat(float initialValue) { bits = new AtomicInteger(Float.floatToIntBits(initialValue)); } public final float addAndGet(float delta){ float expect; float update; do { expect = get(); update = expect + delta; } while(!this.compareAndSet(expect, update)); return update; } public final float getAndAdd(float delta){ float expect; float update; do { expect = get(); update = expect + delta; } while(!this.compareAndSet(expect, update)); return expect; } public final float getAndDecrement(){ return getAndAdd(-1); } public final float decrementAndGet(){ return addAndGet(-1); } public final float getAndIncrement(){ return getAndAdd(1); } public final float incrementAndGet(){ return addAndGet(1); } public final float getAndSet(float newValue) { float expect; do { expect = get(); } while(!this.compareAndSet(expect, newValue)); return expect; } public final boolean compareAndSet(float expect, float update) { return bits.compareAndSet(Float.floatToIntBits(expect), Float.floatToIntBits(update)); } public final void set(float newValue) { bits.set(Float.floatToIntBits(newValue)); } public final float get() { return Float.intBitsToFloat(bits.get()); } public float floatValue() { return get(); } public double doubleValue() { return (double) floatValue(); } public int intValue() { return (int) get(); } public long longValue() { return (long) get(); } public String toString() { return Float.toString(get()); } } 

引用塊內容
http://www.tuicool.com/articles/zuui6z
https://github.com/ysc/word
java程序性能優化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM