標籤:mic jvm icm 需要 float 10個 countdown ring getname
Atomic包的作用:
方便程式員在多線程環境下,無鎖的進行原子操作
Atomic包核心:
Atomic包裡的類基本都是使用Unsafe實現的封裝類,核心操作是CAS原子操作
關於CAS
compare and swap,比較和替換技術,將預期值與當前變數的值比較(compare),如果相等則使用新值替換(swap)當前變數,否則不作操作;
現代CPU已廣泛支援CAS指令,如果不支援,那麼JVM將使用自旋鎖,與互斥鎖一樣,兩者都需先擷取鎖才能訪問共用資源,但互斥鎖會導致線程進入睡眠,而自旋鎖會一直迴圈等待直到擷取鎖;
另外,有一點需要注意的是CAS操作中的ABA問題,即將預期值與當前變數的值比較的時候,即使相等也不能保證變數沒有被修改過,因為變數可能由A變成B再變回A,解決該問題,可以給變數增加一個版本號碼,每次修改變數時版本號碼自增,比較的時候,同時比較變數的值和版本號碼即可
Atomic包主要提供四種原子更新方式
- 原子方式更新基本類型
- 原子方式更新數組
- 原子方式更新引用
- 原子方式更新欄位
原子方式更新基本類型
以下三個類是以原子方式更新基本類型
- AtomicBoolean:原子更新布爾類型。
- AtomicInteger:原子更新整型。
- AtomicLong:原子更新長整型。
以AtomicInteger為例:
package cn.com.example.concurrent.atomic;import java.util.concurrent.atomic.AtomicInteger;/** * Created by Jack on 2017/1/7. */public class AtomicIntegerTest extends Thread { private AtomicInteger atomicInteger; public AtomicIntegerTest(AtomicInteger atomicInteger) { this.atomicInteger = atomicInteger; } @Override public void run() { int i = atomicInteger.incrementAndGet(); System.out.println("generated out number:" + i); } public static void main(String[] args) { AtomicInteger counter = new AtomicInteger(); for (int i = 0; i < 10; i++) {//10個線程 new AtomicIntegerTest(counter).start(); } }}
輸出:
generated out number:1generated out number:2generated out number:3generated out number:4generated out number:5generated out number:6generated out number:7generated out number:8generated out number:9generated out number:10
注意:Atomic包提供了三種基本類型的原子更新,剩餘的Java的基本類型還有char,float和double等,其更新方式可以參考AtomicBoolean的思路來現,AtomicBoolean是把boolean轉成整型再調用compareAndSwapInt進行CAS來實現的,類似的short和byte也可以轉成整形,float和double可以利用Float.floatToIntBits,Double.doubleToLongBits轉成整形和長整形進行相應處理
原子方式更新數組
以下三個類是以原子方式更新數組
- AtomicIntegerArray:原子更新整型數組裡的元素。
- AtomicLongArray:原子更新長整型數組裡的元素。
- AtomicReferenceArray:原子更新參考型別數組裡的元素
以AtomicIntegerArray為例,其方法與AtomicInteger很像,多了個數組下標索引
package cn.com.example.concurrent.atomic;import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicIntegerArray;/** * Created by Jack on 2017/1/7. */public class AtomicIntegerArrayTest { private static int threadCount = 1000; private static CountDownLatch countDown = new CountDownLatch(threadCount); static int[] values = new int[10]; static AtomicIntegerArray ai = new AtomicIntegerArray(values); private static class Counter implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { for (int j = 0; j < 10; j++) {//所有元素+1 ai.getAndIncrement(j); } } countDown.countDown(); } } public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread(new Counter()); } for (int i = 0; i < threadCount; i++) { threads[i].start(); } countDown.await(); for (int i = 0; i < 10; i++) { System.out.println(ai.get(i) + " "); } System.out.println(); for (int i = 0; i < 10; i++) { System.out.println(values[i] + " "); } }}
輸出:
100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 0 0 0 0 0 0 0 0 0 0
需要注意的是,數組value通過構造方法傳遞進去,然後AtomicIntegerArray會將當前數組複製一份,所以當AtomicIntegerArray對內部的數組元素進行修改時,不會影響傳入的數組。
原子方式更新引用
以下三個類是以原子方式更新引用,與其它不同的是,更新引用可以更新多個變數,而不是一個變數
- AtomicReference:原子更新參考型別。
- AtomicReferenceFieldUpdater:原子更新參考型別裡的欄位。
- AtomicMarkableReference:原子更新帶有標記位的參考型別。
以AtomicReference為例
package cn.com.example.concurrent.atomic;import java.util.concurrent.atomic.AtomicReference;/** * Created by Jack on 2017/1/7. */public class AtomicReferenceTest { public static void main(String[] args) { // 建立兩個Person對象,它們的id分別是101和102。 Person p1 = new Person(101); Person p2 = new Person(102); // 建立AtomicReference對象,初始化它的值為p1對象 AtomicReference ar = new AtomicReference(p1); // 通過CAS設定ar。如果ar的值為p1的話,則將其設定為p2。 ar.compareAndSet(p1, p2); Person p3 = (Person) ar.get(); System.out.println("p3 is " + p3); System.out.println("p3.equals(p1)=" + p3.equals(p1)); }}class Person { volatile long id; public Person(long id) { this.id = id; } public String toString() { return "id:" + id; }}
輸出:
p3 is id:102p3.equals(p1)=false
建立AtomicReference對象ar時,將它初始化為p1。
緊接著,通過CAS函數對它進行設定。如果ar的值為p1的話,則將其設定為p2。
最後,擷取ar對應的對象,並列印結果。p3.equals(p1)的結果為false,這是因為Person並沒有覆蓋equals()方法,而是採用繼承自Object.java的equals()方法;而Object.java中的equals()實際上是調用"=="去比較兩個對象,即比較兩個對象的地址是否相等。
原子方式更新欄位
以下三個類是以原子方式更新欄位
- AtomicIntegerFieldUpdater:原子更新整型欄位的更新器。
- AtomicLongFieldUpdater:原子更新長整型欄位的更新器。
- AtomicStampedReference:原子更新帶有版本號碼的參考型別,用於解決使用CAS進行原子更新時,可能出現的ABA問題。
以AtomicIntegerFieldUpdater為例
package cn.com.example.concurrent.atomic;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;/** * Created by Jack on 2017/1/7. */public class AtomicIntegerFieldUpdaterTest { // 建立原子更新器,並設定需要更新的對象類和對象的屬性 private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "old"); public static void main(String[] args) throws InterruptedException { // 設定柯南的年齡是10歲 User conan = new User("conan", 10); // 柯南長了一歲,但是仍然會輸出舊的年齡 System.out.println(a.getAndIncrement(conan)); // 輸出柯南現在的年齡 System.out.println(a.get(conan)); } public static class User { private String name; public volatile int old; public User(String name, int old) { this.name = name; this.old = old; } public String getName() { return name; } public int getOld() { return old; } }}
輸出:
1011
注意: old 需要聲明為 volatile
java.util.concurrent.atomic 包詳解