原子的大概意思就是不可分割,在程序中的表現形式爲一個操做 不可中斷,即使是多線程的狀況下也能夠保證,其中在Java
中就有java.util.concurrent.atomic
包,這個包用來存儲原子類。
原子類的做用與鎖相似,都是爲了在併發場景下保證 線程安全,不過原子類相對於鎖具備如下優點:
CAS
思想的體現。/** * 描述: 演示 AtomicInteger 的基本用法,而且對比非原子類的線程安全問題, * 使用了原子類以後,不須要加鎖也能夠保證線程安全。 */ public class AtomicIntegerDemo1 implements Runnable { private static final AtomicInteger atomicInteger = new AtomicInteger(); public void incrementAtomic() { atomicInteger.getAndIncrement(); } // 添加 volatile 關鍵字保證可見性的同時不保證原子性 private static volatile int basicCount = 0; public void incrementBasic() { basicCount++; } @Override public void run() { for (int i = 0; i < 10000; i++) { incrementAtomic(); incrementBasic(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo1 demo1 = new AtomicIntegerDemo1(); Thread t1 = new Thread(demo1); Thread t2 = new Thread(demo1); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("原子類的結果是:" + atomicInteger.get()); System.out.println("普通變量的結果是:" + basicCount); } }
運行結果:java
/** * 描述: 演示原子數組的使用方法 */ public class AtomicArrayDemo { public static void main(String[] args) throws InterruptedException { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); Decrementer decrementer = new Decrementer(atomicIntegerArray); Incrementer incrementer = new Incrementer(atomicIntegerArray); Thread[] threadsIncrementer = new Thread[100]; Thread[] threadsDecrementer = new Thread[100]; for (int i = 0; i < 100; i++) { threadsDecrementer[i] = new Thread(decrementer); threadsIncrementer[i] = new Thread(incrementer); threadsDecrementer[i].start(); threadsIncrementer[i].start(); } for (int i = 0; i < 100; i++) { threadsDecrementer[i].join(); threadsIncrementer[i].join(); } for (int i = 0; i < atomicIntegerArray.length(); i++) { if (atomicIntegerArray.get(i) != 0) { System.out.println("發現了非0值" + i); } } System.out.println("運行結束"); } } class Decrementer implements Runnable { private AtomicIntegerArray array; public Decrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { for (int i = 0; i < array.length(); i++) { array.getAndDecrement(i); } } } class Incrementer implements Runnable { private AtomicIntegerArray array; public Incrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { for (int i = 0; i < array.length(); i++) { array.getAndIncrement(i); } } }
運行結果:數組
由於使用的是原子數組,因此不管運行多少次都不會出現非0的狀況。安全
AtomicReference
類的做用和AtomicInteger
並無什麼區別,AtomicInteger
能夠 讓一個整數保證原子性,而AtomicReference
可讓一個 對象保證原子性。由於對象中能夠包含多個屬性,因此AtomicReference
比AtomicInteger
功能要強一些。
/** * 描述: 演示自旋鎖 */ public class SpinLock { // 聲明自旋鎖 private AtomicReference<Thread> sign = new AtomicReference<>(); /** * 加鎖 */ public void lock() { Thread current = Thread.currentThread(); // 使用 while 循環加 CAS 操做實現自旋 // 但願沒有線程持有鎖,傳入 null // 但願更新的值是本身,傳入 current while (!sign.compareAndSet(null, current)) { System.out.println(Thread.currentThread().getName() + "自旋獲取失敗,再次嘗試"); } } /** * 解鎖 */ public void unlock() { Thread current = Thread.currentThread(); // 解鎖首先要有鎖,因此一次就能夠解掉,不須要 while 循環 // 但願持有鎖的線程是本身,傳入 current // 由於是解鎖,因此要更新的值爲 null sign.compareAndSet(current, null); } public static void main(String[] args) { SpinLock spinLock = new SpinLock(); Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "開始嘗試獲取自旋鎖"); spinLock.lock(); System.out.println(Thread.currentThread().getName() + "獲取到了自旋鎖"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLock.unlock(); System.out.println(Thread.currentThread().getName() + "釋放了自旋鎖"); } } }; // 用兩個線程執行任務,模擬爭搶 Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); thread2.start(); } }
原子變量在保證線程安全的同時開銷也要比普通變量大得多,由於須要維護內部的一些邏輯,因此若是在須要的時候將普通變量升級爲原子變量的話,就能夠大大的節省系統資源,提高系統執行效率,下面就來演示一下將普通變量升級爲原子變量。
/** * 描述: 演示 FieldUpdater 的用法 */ public class AtomicIntegerFieldUpdaterDemo implements Runnable { static Candidate tom; static Candidate peter; public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score"); @Override public void run() { for (int i = 0; i < 10000; i++) { peter.score++; // 經過 scoreUpdater 將普通變量升級爲原子變量 scoreUpdater.getAndIncrement(tom); } } public static class Candidate { volatile int score; } public static void main(String[] args) throws InterruptedException { AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo(); tom = new Candidate(); peter = new Candidate(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); t1.join(); t1.join(); System.out.println("普通的變量: " + peter.score); System.out.println("升級後的變量: " + tom.score); } }
運行結果:多線程
int
類型的字段,int
的包裝類也不能夠volatile
修飾private
訪問權限static
關鍵字因爲long
的字節數比int
要多,因此在使用AtomicLong
時的效率要比AtomicInteger
要低一些,針對這個狀況,在 Java 8 中引入了一個新的類 Adder累加器,在高併發場景下LongAdder
的開銷要比AtomicLong
效率要高,不過本質是 以空間換時間。當競爭激烈的時候,LongAdder
把不一樣線程對應到不一樣的Cell
上進行修改,下降了衝突的機率,這也是 多段鎖的理念,從而提高了併發性。
/** * 描述: 演示高併發場景下 LongAdder 要比 AtomicLong 要好。 */ public class AtomicLongDemo { public static void main(String[] args) throws InterruptedException { AtomicLong counter = new AtomicLong(0); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println(counter.get()); System.out.println("AtomicLong 耗時爲:" + (endTime - startTime) + " ms"); } private static class Task implements Runnable { private AtomicLong counter; public Task(AtomicLong counter) { this.counter = counter; } @Override public void run() { for (int i = 0; i < 10000; i++) { counter.incrementAndGet(); } } } }
運行結果:併發
/** * 描述: 演示高併發場景下 LongAdder 要比 AtomicLong 要好。 */ public class LongAdderDemo { public static void main(String[] args) throws InterruptedException { LongAdder counter = new LongAdder(); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println(counter.sum()); System.out.println("LongAdder 耗時爲:" + (endTime - startTime) + " ms"); } private static class Task implements Runnable { private LongAdder counter; public Task(LongAdder counter) { this.counter = counter; } @Override public void run() { for (int i = 0; i < 10000; i++) { counter.increment(); } } } }
運行結果:ide
在使用AtomicLong
進行累加時,每一個線程之間須要將使用共享內存進行通訊,即線程執行操做時只能在工做內存中進行,而後將值刷新到主內存,在高併發場景下頻繁的刷新和獲取操做會帶來必定的性能開銷。高併發
LongAdderLongAdder
引入了分段累加的概念,在內部有一個base
變量和Cell[]
數組共同參與計數:工具
Cell[i]
中。public long sum() { Cell[] as = cells; Cell a; long sum = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
首先將負責計數的cells
賦值給as
,這樣就能夠經過as
來獲取數值了,base
用於併發不激烈的狀況下,這裏使用了sum
進行統計。接下來是判斷as
的是否爲空,若是爲空說明根本沒有向Cell
中存儲數,因此直接返回sum
(base)便可,若是用到了cell
就將它作遍歷,並將值從中取出,並用sum
進行統計。源碼分析
總結:在低爭用下,AtomicLong
和LongAdder
這兩個類具備相同的特性。可是在競爭激烈的狀況下,LongAdder
的預期吞吐量要高得多,可是消耗的空間也多。性能
LongAdder
適合的場景是統計求和計數的場景,並且LongAdder
基本只提供add()
方法,而AtomicLong
還具備CAS
方法。
Accumulator
和Adder
很是類似,Accumulator
是一個更通用版的Adder
。
/** * 描述: 演示 LongAccumulator 的用法 */ public class LongAccumulatorDemo { public static void main(String[] args) { LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0); ExecutorService executorService = Executors.newFixedThreadPool(8); // 從0開始,從1加到9 IntStream.range(1, 10) .forEach(i -> executorService.submit(() -> longAccumulator.accumulate(i))); executorService.shutdown(); while (!executorService.isTerminated()) { } System.out.println(longAccumulator.getThenReset()); } }
運行結果:
參考連接:
慕課網之玩轉Java併發工具,精通JUC,成爲併發多面手