atomic的实现原理
? ?
? ? ?在多線程的場景中,我們需要保證數(shù)據(jù)安全,就會考慮同步的方案,通常會使用synchronized或者lock來處理,使用了synchronized意味著內(nèi)核態(tài)的一次切換。這是一個很重的操作。
? ? ?有沒有一種方式,可以比較便利的實現(xiàn)一些簡單的數(shù)據(jù)同步,比如計數(shù)器等等。concurrent包下的atomic提供我們這么一種輕量級的數(shù)據(jù)同步的選擇。
class MyThread implements Runnable {static AtomicInteger ai=new AtomicInteger(0);public void run() {for (int m = 0; m < 1000000; m++) {ai.getAndIncrement();}} };public class TestAtomicInteger {public static void main(String[] args) throws InterruptedException {MyThread mt = new MyThread();Thread t1 = new Thread(mt);Thread t2 = new Thread(mt);t1.start();t2.start();Thread.sleep(500);System.out.println(MyThread.ai.get());} }? ? ? 在以上代碼中,使用AtomicInteger聲明了一個全局變量,并且在多線程中進行自增,代碼中并沒有進行顯示的加鎖。以上代碼的輸出結果,永遠都是2000000。如果將AtomicInteger換成Integer,打印結果基本都是小于2000000。
? ? ? 也就說明AtomicInteger聲明的變量,在多線程場景中的自增操作是可以保證線程安全的。接下來我們分析下其原理。
原理
? ? ? 這里,我們來看看AtomicInteger是如何使用非阻塞算法來實現(xiàn)并發(fā)控制的。
? ? ? AtomicInteger的關鍵域只有一下3個:
? ? ? 這里, unsafe是java提供的獲得對對象內(nèi)存地址訪問的類,注釋已經(jīng)清楚的寫出了,它的作用就是在更新操作時提供“比較并替換”的作用。實際上就是AtomicInteger中的一個工具。
? ? ? valueOffset是用來記錄value本身在內(nèi)存的編譯地址的,這個記錄,也主要是為了在更新操作在內(nèi)存中找到value的位置,方便比較。
? ? ?value是用來存儲整數(shù)的時間變量,這里被聲明為volatile。volatile只能保證這個變量的可見性。不能保證他的原子性。
? ? ?可以看看getAndIncrement這個類似i++的函數(shù),可以發(fā)現(xiàn),是調(diào)用了UnSafe中的getAndAddInt。
/*** Atomically increments by one the current value.** @return the previous value*/public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}/*** Atomically sets to the given value and returns the old value.** @param newValue the new value* @return the previous value*/public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}public final int getAndSetInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);//使用unsafe的native方法,實現(xiàn)高效的硬件級別CAS} while(!this.compareAndSwapInt(var1, var2, var5, var4));return var5;}? ? ? 如何保證原子性:自旋 + CAS(樂觀鎖)。在這個過程中,通過compareAndSwapInt比較更新value值,如果更新失敗,重新獲取舊值,然后更新。
優(yōu)缺點
? ? ? CAS相對于其他鎖,不會進行內(nèi)核態(tài)操作,有著一些性能的提升。但同時引入自旋,當鎖競爭較大的時候,自旋次數(shù)會增多。cpu資源會消耗很高。
? ? ? 換句話說,CAS+自旋適合使用在低并發(fā)有同步數(shù)據(jù)的應用場景。
?
Java 8做出的改進和努力
? ? ? 在Java 8中引入了4個新的計數(shù)器類型,LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator。他們都是繼承于Striped64。
? ? ?在LongAdder 與AtomicLong有什么區(qū)別?
Atomic*遇到的問題是,只能運用于低并發(fā)場景。因此LongAddr在這基礎上引入了分段鎖的概念。可以參考《JDK8系列之LongAdder解析》一起看看做了什么。
? ? ?大概就是當競爭不激烈的時候,所有線程都是通過CAS對同一個變量(Base)進行修改,當競爭激烈的時候,會將根據(jù)當前線程哈希到對于Cell上進行修改(多段鎖)。
/*** Table of cells. When non-null, size is a power of 2.*/transient volatile Cell[] cells;/*** Base value, used mainly when there is no contention, but also as* a fallback during table initialization races. Updated via CAS.*/transient volatile long base; /*** Padded variant of AtomicLong supporting only raw accesses plus CAS.** JVM intrinsics note: It would be possible to use a release-only* form of CAS here, if it were provided.*/@sun.misc.Contended static final class Cell {volatile long value;Cell(long x) { value = x; }final boolean cas(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);}// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long valueOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> ak = Cell.class;valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));} catch (Exception e) {throw new Error(e);}}}? ? ?可以看到大概實現(xiàn)原理是:通過CAS樂觀鎖保證原子性,通過自旋保證當次修改的最終修改成功,通過降低鎖粒度(多段鎖)增加并發(fā)性能。
?
?
Refrence:
https://mp.weixin.qq.com/s/aw6OXC9wkxH42rCywNd7yQ
?
?
總結
以上是生活随笔為你收集整理的atomic的实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ffmpeg编程查看视频文件信息
- 下一篇: C语言 | 条件运算符