日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JUC中的Atomic原子类

發布時間:2023/12/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JUC中的Atomic原子类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Atomic 原子類
    • 1. 原子類介紹
    • 2. 基本類型原子類
    • 3. 數組類型原子類
    • 4. 引用類型原子類
    • 5. 升級類型原子類
    • 6. Adder 累加器
    • 7. Accumulator 累加器

Atomic 原子類


1. 原子類介紹

  • 不可分割的

  • 一個操作是不可中斷的,即使多線程的情況下也可以保證, 即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其他線程干擾。

  • 原子類的作用和鎖類似,是為了保證并發情況下線程安全,不過相比于鎖,更有優勢

    優勢: 粒度更細,效率更高

  • 原子類縱覽:

    類型Value
    Atomic*基本類型原子類AtomicInteger
    AtomicLong
    AtomicBoolean
    Atomic*Arrays數組類型原子類AtomicIntegerArray
    AtomicLongArray
    AtomicReferenceArray
    Atomic*Reference引用類型原子類AtomicReference
    AtomicStampedReference
    AtomicMarkableReference
    Atomic*Fieldupdate升級類型原子類AtomicIntegerFieldUpdater
    AtomicLongFieldUpdater
    AtomicReferenceFieldUpdater
    Adder累加器LongAdder
    DoubleAdder
    Accumulator累加器LongAccumulator
    DoubleAccumulator

    2. 基本類型原子類


    • AtomicInteger :整型原子類
    • AtomicLong :長整型原子類
    • AtomicBoolean :布爾型原子類

    以 AtomicInteger 為例

    AtomicInteger 類常用方法 :

    public final int get() //獲取值public final void set(int newValue) // 設置值public final void lazySet(int newValue) //最終設置為給定的值public final int getAndSet(int newValue) // 獲取當前值,并設置新值public final int getAndIncrement() //獲取當前值 并自增public final int getAndDecrement() //獲取當前的值,并自減public final int getAndAdd(int delta) // 獲取當前值,并加上預期值public final int getAndAdd(int delta) // 獲取當前值,并加上預期值public final boolean compareAndSet(int expect, int update) //比較并替換

    使用 :

    import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {public static void main(String[] args) {int temvalue = 0;AtomicInteger i = new AtomicInteger(0);temvalue = i.getAndSet(3);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3temvalue = i.getAndIncrement();System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4temvalue = i.getAndAdd(5);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9}}

    案例 :

    public class Test {public static void main(String[] args) {TestDemo thread = new TestDemo();Thread t1 = new Thread(thread,"窗口一");Thread t2 = new Thread(thread,"窗口二");t1.start();t2.start();} }class TestDemo implements Runnable{//共享的火車票變量private int count = 100;//重寫run方法@Overridepublic void run() {while (count > 0){try {//休眠一下 方便出現并發問題Thread.sleep(50);}catch (Exception e){e.getMessage();}sale();}}//賣票public void sale(){if(count > 0){System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));count--;}}}

    窗口一出售 :37
    窗口二出售 :39
    窗口一出售 :40
    窗口二出售 :41
    窗口一出售 :41
    窗口一出售 :43
    窗口二出售 :43
    窗口一出售 :45
    窗口二出售 :45
    窗口一出售 :47
    窗口二出售 :47

    多線程下會出現重復賣票的情況,我們解決這個問題可以使用 JDK 內置鎖 (Synchronized)保證線程原子性,當某個線程取到鎖后,其他線程就會等待,但是性能低下,我們可以使用AtomicInteger類,是一個專門提供可以保證原子性的類

    package com.dimple.test;import java.util.concurrent.atomic.AtomicInteger;public class Test5 {public static void main(String[] args) {TestDemo thread = new TestDemo();Thread t1 = new Thread(thread,"窗口一");Thread t2 = new Thread(thread,"窗口二");t1.start();t2.start();} }class TestDemo implements Runnable{//共享的火車票變量private static AtomicInteger atomic = new AtomicInteger(100);//重寫run方法@Overridepublic void run() {while (atomic.get() > 0){try {//休眠一下 方便出現并發問題Thread.sleep(50);}catch (Exception e){e.getMessage();}sale();}}//賣票public void sale(){if(atomic.get() > 0){Integer count= 100 - atomic.getAndDecrement() + 1; //使用底層方法getAndDecrement() 自-1;System.out.println(Thread.currentThread().getName()+ "," + count);//獲取當前值}} }

    窗口一出售 :91
    窗口二出售 :92
    窗口一出售 :93
    窗口二出售 :94
    窗口一出售 :95
    窗口二出售 :96
    窗口一出售 :97
    窗口二出售 :98
    窗口一出售 :99
    窗口二出售 :100

    原理分析 :

    // setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}
  • AtomicInteger 類主要利用 CAS(compare and swap) + volatile native 方法來保證原子操作,從而避免 synchronized 的高開銷,執行效率大為提升。
  • CAS(compare and swap) 的原理是那期望值和原本的一個值作比較,如果相同則更新成新的值, UnSafe 類的 objectFieldOffset() 方法是一個本地方法,這個方法是用來拿到 “原來的值” 的內存地址。并且 value 是一個 volatile變量,在內存中可見,因此 JVM可以保證任何時刻線程總能拿到該變量的最新值。

  • 3. 數組類型原子類


    • AtomicIntegerArray :整形數組原子類
    • AtomicLongArray :長整形數組原子類
    • AtomicReferenceArray :引用類型數組原子類

    以 AtomicIntegerArray 為例

    AtomicIntegerArray 類常用方法

    public final int get(int i) //獲取值public final void set(int i, int newValue) // 設置值public final void lazySet(int i, int newValue) //最終集的元素在位置 i到給定值。public final int getAndSet(int i, int newValue) //自動設置元素的位置 i到給定值并返回舊值。public final boolean compareAndSet(int i, int expect, int update) //自動設置元素的位置 i給更新后的值,如果預期值 ==期望值。public final int getAndIncrement(int i) //自動遞增一個指數 i元素。public final int getAndDecrement(int i) //自動遞減指數 i元素

    使用 :

    import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayTest {public static void main(String[] args) {int temvalue = 0;int[] nums = { 1, 2, 3, 4, 5, 6 };AtomicIntegerArray i = new AtomicIntegerArray(nums);for (int j = 0; j < nums.length; j++) {System.out.println(i.get(j));}temvalue = i.getAndSet(0, 2);System.out.println("temvalue:" + temvalue + "; i:" + i);temvalue = i.getAndIncrement(0);System.out.println("temvalue:" + temvalue + "; i:" + i);temvalue = i.getAndAdd(0, 5);System.out.println("temvalue:" + temvalue + "; i:" + i);}}

    案例 :

    public class AtmoicArray {public static void main(String[] args) throws InterruptedException {AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(1000);Decrement decrement = new Decrement(atomicIntegerArray);Increment increment = new Increment(atomicIntegerArray);Thread[] threads= new Thread[100];Thread[] threads2= new Thread[100];for (int i = 0; i < 100 ; i++) {threads2[i]=new Thread(decrement);threads[i]=new Thread(increment);threads2[i].start();threads[i].start();}for (int i = 0; i < 100 ; i++) {threads2[i].join();threads[i].join();}for (int i = 0; i < atomicIntegerArray.length(); i++) {if(atomicIntegerArray.get(i)!=0) {System.out.println("發現非0值" + i);}}System.out.println("運行結束");}}class Decrement implements Runnable{private AtomicIntegerArray array;Decrement(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length() ; i++) {array.getAndDecrement(i);}} }class Increment implements Runnable{private AtomicIntegerArray array;Increment(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length() ; i++) {array.getAndIncrement(i);}} }

    運行結束

    運行結果 ,會發現我們數組線程每次 加100 減100 ,并不會出現不等于0的數據,數據并沒有出現錯亂,AtomicIntegerArray 給我們提供了數組的原子性

    原理分析 :

    private static final Unsafe unsafe = Unsafe.getUnsafe();private static final int base = unsafe.arrayBaseOffset(int[].class);private static final int shift;private final int[] array;static {int scale = unsafe.arrayIndexScale(int[].class);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");shift = 31 - Integer.numberOfLeadingZeros(scale);}private long checkedByteOffset(int i) {if (i < 0 || i >= array.length)throw new IndexOutOfBoundsException("index " + i);return byteOffset(i);}private static long byteOffset(int i) {return ((long) i << shift) + base;}
  • unsafe.arrayBaseOffset 獲取該類型的數組,在對象存儲時,存放第一個元素的內存地址,相對于數組對象起始的內存偏移量,unsafe.arrayIndexSacle(int[].class) 獲取該類型的數組中元素的大小,占用多少個字節
  • 根據scale ,base 定位到任意一個下標的地址 舉例 : int scale = 4;1個int類型,在java中占用4個字節, Integer.numberOfLeadingZeros(scale); 返回 scale 高位連續0的個數,得出shift = 2, 而shift在如下方法使用,shift就是用來定位數組中的內存位置,用來移位

  • 4. 引用類型原子類


    基本類型原子類只能更新一個變量,如果需要原子更新多個變量,需要使用引用類型原子類

    • AtomicReference :引用類型原子類
    • AtomicStampedReference :原子更新帶有版本號的引用類型
    • AtomicMarkableReference :原子更新帶有標記的引用類型

    AtomicReference 常用方法

    public final V get() //獲取值public final void set(V newValue) //設置值public final void lazySet(V newValue) //最終設置為給定的值public final boolean compareAndSet(V expect, V update) //自動設置的值來指定更新值public final V getAndSet(V newValue) //自動設置為給定的值并返回舊值。public final V getAndUpdate(UnaryOperator<V> updateFunction) //自動更新當前值與結果應用給定的函數,返回前一個值。

    AtomicReference 使用

    public class Test {public static void main(String[] args) {AtomicReference<Person> atomicReference = new AtomicReference<Person>();Person person = new Person("abc", 22);atomicReference.set(person);Person updatePerson = new Person("Daisy", 20);atomicReference.compareAndSet(person, updatePerson);System.out.println(atomicReference.get().getName());System.out.println(atomicReference.get().getAge());}}@Data class Person {private String name;private int age;}

    Daisy
    20

    AtomicStampedReference 使用

    public class Test {public static void main(String[] args) {// 實例化、取當前值和 stamp 值final Integer initialRef = 0, initialStamp = 0;final AtomicStampedReference<Integer> asr = new AtomicStampedReference<Integer>(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());// compare and setfinal Integer newReference = 666, newStamp = 999;final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp);System.out.println("currentValue=" + asr.getReference()+ ", currentStamp=" + asr.getStamp()+ ", casResult=" + casResult);// 獲取當前的值和當前的 stamp 值int[] arr = new int[1];final Integer currentValue = asr.get(arr);final int currentStamp = arr[0];System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);// 單獨設置 stamp 值final boolean attemptStampResult = asr.attemptStamp(newReference, 88);System.out.println("currentValue=" + asr.getReference()+ ", currentStamp=" + asr.getStamp()+ ", attemptStampResult=" + attemptStampResult);// 重新設置當前值和 stamp 值asr.set(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());}

    currentValue=0, currentStamp=0
    currentValue=666, currentStamp=999, casResult=true
    currentValue=666, currentStamp=999
    currentValue=666, currentStamp=88, attemptStampResult=true
    currentValue=0, currentStamp=0


    AtomicMarkableReference 使用

    public class Test {public static void main(String[] args) {// 實例化、取當前值和 mark 值final Boolean initialRef = null, initialMark = false;final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference<Boolean>(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());// compare and setfinal Boolean newReference1 = true, newMark1 = true;final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1);System.out.println("currentValue=" + amr.getReference()+ ", currentMark=" + amr.isMarked()+ ", casResult=" + casResult);// 獲取當前的值和當前的 mark 值boolean[] arr = new boolean[1];final Boolean currentValue = amr.get(arr);final boolean currentMark = arr[0];System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark);// 單獨設置 mark 值final boolean attemptMarkResult = amr.attemptMark(newReference1, false);System.out.println("currentValue=" + amr.getReference()+ ", currentMark=" + amr.isMarked()+ ", attemptMarkResult=" + attemptMarkResult);// 重新設置當前值和 mark 值amr.set(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());}

    currentValue=null, currentMark=false
    currentValue=true, currentMark=true, casResult=true
    currentValue=true, currentMark=true
    currentValue=true, currentMark=false, attemptMarkResult=true
    currentValue=null, currentMark=false

    5. 升級類型原子類

    • AtomicIntegerFieldUpdater : 原子更新整形字段的更新器
    • AtomicLongFieldUpdater :原子更新長整形字段的更新器
    • AtomicReferenceFieldUpdater :原子更新引用類型里的字段的更新器

    AtomicIntegerFieldUpdater 常用方法

    public final V get() //獲取值public final void set(V newValue) //設置值public final void lazySet(V newValue) //最終設置為給定的值public final boolean compareAndSet(V expect, V update) //自動設置的值來指定更新值public final V getAndSet(V newValue) //自動設置為給定的值并返回舊值。public final V getAndUpdate(UnaryOperator<V> updateFunction) //自動更新當前值與結果應用給定的函數,返回前一個值。public final V getAndAccumulate(T obj, V x,BinaryOperator<V> accumulatorFunction) //自動更新與應用給出的函數的值與給定值的結果指標 i元素,返回前一個值。public final V accumulateAndGet(T obj, V x,BinaryOperator<V> accumulatorFunction) //自動更新與應用給出的函數的值與給定值的結果指標 i元素,返回更新后的值。

    AtomicIntegerFieldUpdater 使用

    public class Test {public static void main(String[] args) {AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");User user = new User("Java", 22);System.out.println(a.getAndIncrement(user));// 22System.out.println(a.get(user));// 23} }@Data class User {private String name;public volatile int age; }

    22
    23

    要想原子地更新對象的屬性需要兩步。第一步,因為對象的屬性修改類型原子類都是抽象類,所以每次使用都必須使用靜態方法 newUpdater()創建一個更新器,并且需要設置想要更新的類和屬性。第二步,更新的對象屬性必須使用 public volatile 修飾符。

    6. Adder 累加器

    • 是JDK 1.8 中 引入的一個比較新的類
    • 高并發的情況下 LongAdder 比 AtomitLong 效率高,不過是空間換時間
    • 競爭激烈的時候,LingAdder 把不同的線程對應到不同的cell 上修改,降低了沖突的概率,是多段鎖的理念,提高了并發性

    測試 AtomicLong的性能

    import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong;/*** 演示高并發情況下LongAdder 比 AtomicLong* 性能好*/ public class AtomicLongDemo {public static void main(String[] args) throws InterruptedException {AtomicLong atomicLong = new AtomicLong(0);//線程池開始時間long start = System.currentTimeMillis();ExecutorService executorService = Executors.newFixedThreadPool(20);for (int i = 0; i < 10000; i++) {executorService.submit(new Task(atomicLong));}//表示線程池執行完畢executorService.shutdown();while (!executorService.isTerminated()){}long end = System.currentTimeMillis();System.out.println(atomicLong.get());System.out.println("耗時"+(end -start));}public static class Task implements Runnable{private AtomicLong atomicLong;public Task(AtomicLong atomicLong) {this.atomicLong = atomicLong;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {atomicLong.incrementAndGet();}}} }

    測試 LongAdder 性能

    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder;/*** 演示高并發情況下LongAdder 比 AtomicLong* 性能好*/ public class LongAdderDemo {public static void main(String[] args) throws InterruptedException {LongAdder atomicLong = new LongAdder();//線程池開始時間long start = System.currentTimeMillis();ExecutorService executorService = Executors.newFixedThreadPool(20);for (int i = 0; i < 10000; i++) {executorService.submit(new Task(atomicLong));}//表示線程池執行完畢executorService.shutdown();while (!executorService.isTerminated()){}long end = System.currentTimeMillis();System.out.println(atomicLong.sum() );System.out.println("耗時"+(end -start));}public static class Task implements Runnable{private LongAdder longAdder;public Task(LongAdder longAdder) {this.longAdder = longAdder;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {longAdder.increment();}}} }

    會發現 LongAdder 比 AtomicLong 快了好多

  • 他們內部實現有些不同,AtomicLong每次加法都需要同步,所以沖突比較多,也就降低了效率
  • 而 LongAdder ,每個線程都有自己的計數器,僅用來線程計數,不會和其他線程打擾
  • AtomicLong引入了分段鎖的概念,內部有一個base變量 和 cell[] 數組共同參與計數
  • base變量:競爭不激烈,直接累加到該變量上
  • cell [] 數組: 競爭激烈,各個線程分累加到自己到cell[i] 卡槽中
  • 7. Accumulator 累加器

    • Accumualtor 和 Adder 非常相似,Accumualtor就是更通用的版本Adder
    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAccumulator; import java.util.stream.IntStream;public class LongAccumulatorDemo {public static void main(String[] args) {LongAccumulator accumulator = new LongAccumulator((x,y)->x+y,0);ExecutorService executorService = Executors.newFixedThreadPool(8);IntStream.range(1,10).forEach(i->executorService.submit(()->accumulator.accumulate(i)));executorService.shutdown();while (!executorService.isTerminated())System.out.println(accumulator.getThenReset());}

    45


    個人博客地址:http://blog.yanxiaolong.cn ? | 『縱有疾風起,人生不言棄』

    總結

    以上是生活随笔為你收集整理的JUC中的Atomic原子类的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。