最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的原子類詳解

 更新時間:2023年10月24日 08:29:47   作者:似寒若暖  
這篇文章主要介紹了Java中的原子類詳解,Java原子類是一種多線程編程中常用的工具,用于實現線程安全的操作,它們提供了一種原子性操作的機制,確保多個線程同時訪問共享變量時的數據一致性,需要的朋友可以參考下

Unsafe

概述

在將原子類之前,先介紹下Unsafe類,因為原子類的操作都是基于該類做的

Unsafe對象提供了非常底層操作內存、線程的方法

該類只提供了一個私有的無參構造,且Unsafe的定義是一個的私有成員變量,源碼如下

public final class Unsafe {
	 //私有成員靜態(tài)變量
    private static final Unsafe theUnsafe;
    
    // 構造器
    private Unsafe() {
    }

由上一點可知Unsafe對象只能通過反射獲取,不能直接創(chuàng)建,獲取方式如下

public class UnsafeUtil {
    static Unsafe unsafe;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException("獲取 Unsafe 對象異常");
        }
    }
    static Unsafe getUnsafe() {
        return unsafe;
    }
}

Unsafe的cas

Unsafe的cas操作是通過對象屬性的偏移量、舊值、新值

要操作的屬性還需要使用volatile修飾,否則會報非法參數異常

示例如下

public class UnsafeCas {
    public static void main(String[] args) throws NoSuchFieldException {
        Dog dog = new Dog();
        Unsafe unsafe = UnsafeUtil.getUnsafe();
        // 通過反射獲取類中屬性的域對象 --> 供Unsafe對象獲取域對象偏移量使用
        Field id = Dog.class.getDeclaredField("id");
        Field age = Dog.class.getDeclaredField("age");
        Field name = Dog.class.getDeclaredField("name");
        // 根據域對象獲取域對象的偏移量 --> 供Unsafe cas操作時使用
        long idOffset = unsafe.objectFieldOffset(id);
        long ageOffset = unsafe.objectFieldOffset(age);
        long nameOffset = unsafe.objectFieldOffset(name);
        // Unsafe cas 操作, 參數為要操作對象,屬性偏移量,舊值,新值
        // 如果要保證并發(fā),在加上while(true)循環(huán)判斷該cas操作是否成功來控制循環(huán)退出條件即可
        unsafe.compareAndSwapInt(dog,idOffset,0,1);
        unsafe.compareAndSwapInt(dog,ageOffset,0,5);
        unsafe.compareAndSwapObject(dog,nameOffset,null,"狗狗");
        System.out.println(dog);
    }
}

class Dog{
    volatile long id;
    volatile int age;
    volatile String name;

    @Override
    public String toString() {
        return "Dog{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

原子類

基本類型包裝類-原子類

  • AtomicBoolean(原子Boolean類)
  • AtomicInteger(原子整形類)
  • AtomicLong(原子長整型類)

以AtomicInteger為例介紹下該組相關API

public class AtomicIntegerApi {

    public static void main(String[] args) {
        AtomicInteger i = new AtomicInteger(0);

        // 類似于 i++,獲取并自增(i = 0, 結果 i = 1, 返回 0)
        System.out.println(i.getAndIncrement());
        System.out.println(i.get());

        // 類似于 ++i,自增并獲?。╥ = 1, 結果 i = 2, 返回 2)
        System.out.println(i.incrementAndGet());
        System.out.println(i.get());

        // 類似于 --i,自減并獲?。╥ = 2, 結果 i = 1, 返回 1)
        System.out.println(i.decrementAndGet());
        System.out.println(i.get());

        // 類似于 i--,獲取并自減(i = 1, 結果 i = 0, 返回 1)
        System.out.println(i.getAndDecrement());
        System.out.println(i.get());

        /*
         * 上述加減操作單元都是1,如果想自定義操作單元,可以用下面方法
         * */
        // 獲取并加值(i = 0, 結果 i = 5, 返回 0)
        System.out.println(i.getAndAdd(5));
        System.out.println(i.get());

        // 加值并獲?。╥ = 5, 結果 i = 0, 返回 0)
        System.out.println(i.addAndGet(-5));
        System.out.println(i.get());

        /*
        * 上述均為加減操作,若想進行更新操作可以使用如下方法,
        * 入參為一元整型操作函數式接口: IntUnaryOperator updateFunction
        * */
        // 獲取并更新(i = 0, p 為 i 的當前值, 結果 i = -2, 返回 0)
        // 其中函數中的操作能保證原子,但函數需要無副作用
        System.out.println(i.getAndUpdate(p -> p - 2));
        System.out.println(i.get());

        // 更新并獲?。╥ = -2, p 為 i 的當前值, 結果 i = 0, 返回 0)
        // 其中函數中的操作能保證原子,但函數需要無副作用
        System.out.println(i.updateAndGet(p -> p + 2));
        System.out.println(i.get());


        /*
         * 若想進行兩個參數測操作,可以使用如下方法
         * 入參為:參數一;要操作的第二個整除 ,參數二:一元整型操作函數式接口: IntUnaryOperator updateFunction
         * */
        // 獲取并計算(i = 0, p 為 i 的當前值, x 為參數1, 結果 i = 10, 返回 0)
        // 其中函數中的操作能保證原子,但函數需要無副作用
        // getAndUpdate 如果在 lambda 中引用了外部的局部變量,要保證該局部變量是 final 的
        // getAndAccumulate 可以通過 參數1 來引用外部的局部變量,但因為其不在 lambda 中因此不必是 final
        System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));
        System.out.println(i.get());

        // 計算并獲?。╥ = 10, p 為 i 的當前值, x 為參數1, 結果 i = 0, 返回 0)
        // 其中函數中的操作能保證原子,但函數需要無副作用
        System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));
        System.out.println(i.get());

    }
}

引用類-原子類

  • AtomicReference(基礎原子引用類)
  • AtomicMarkableReference(可以判斷共享變量是否修改過的原子引用類)
  • AtomicStampedReference(帶戳的原子引用類)

AtomicReference類的使用及問題 

public class AtomicReferenceUser {
    static AtomicReference<String> ref = new AtomicReference<>("A");
    public static void main(String[] args){
        System.out.println("main start...");
        // 獲取值 A
        String prev = ref.get();
        // 嘗試改為 C
        System.out.println("change A->C {}" + ref.compareAndSet(prev, "C"));
    }
}

使用基本原子引用類,會引發(fā)ABA問題:也就是如下場景,如果一個線程t1在執(zhí)行開始時獲取到共享變量為A,t1線程執(zhí)行,這是t2線程將共享變量由A->B并執(zhí)行結束,又有一個線程t3將B->A并執(zhí)行結束,這是線程t1要將共享變量由A變?yōu)镃,這時是會成功的。代碼如下

public class ABAThreadSafeQuestion {
    static AtomicReference<String> ref = new AtomicReference<>("A");
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main start...");
        // 獲取值 A
        // 這個共享變量被它線程修改過?
        String prev = ref.get();
        other();
        TimeUnit.SECONDS.sleep(1);
        // 嘗試改為 C
        System.out.println("change A->C {}" + ref.compareAndSet(prev, "C"));
    }

    private static void other() {
        new Thread(() -> {
            System.out.println("change A->B {}" + ref.compareAndSet(ref.get(), "B"));
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            System.out.println("change B->A {}" + ref.compareAndSet(ref.get(), "A"));
        }, "t2").start();
    }
}

AtomicMarkableReference類的使用及問題 使用AtomicReference原子類操作是會引發(fā)ABA問題,如果我只關心共享變量是否被修改過,則可以使用AtomicMarkableReference原子類來解決ABA問題,其他線程修改時,原子變量的第二個參數

public class AtomicMarkableReferenceSloveABA {
    static AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A",Boolean.TRUE);
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main start...");
        // 獲取值 A
        String prev = ref.getReference();
        other();
        TimeUnit.SECONDS.sleep(1);
        // 嘗試改為 C
        System.out.println("change A->C {}" + ref.compareAndSet(prev, "C",Boolean.TRUE,Boolean.FALSE));
    }

    private static void other() {
        new Thread(() -> {
            System.out.println("change A->B {}" + ref.compareAndSet(ref.getReference(),"B",Boolean.TRUE,Boolean.FALSE));
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            System.out.println("change B->A {}" + ref.compareAndSet(ref.getReference(), "A",Boolean.TRUE,Boolean.FALSE));
        }, "t2").start();
    }
}

AtomicStampedReference原子類的使用及問題

AtomicMarkableReference原子類雖然可以解決ABA問題,但是如果需要知道具體修改過幾次,那么 AtomicMarkableReference原子類就不能滿足需求了,若要解決這個問題,則就要使用AtomicStampedReference原子類

public class ThreadSafeImplByAtomicStampedReference{

    static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main start...");
        // 獲取值A
        String prev = ref.getReference();
        // 獲取版本號
        int stamp = ref.getStamp();
        System.out.println("版本 {}" + stamp);
        // 如果中間有其它線程干擾,發(fā)生了 ABA 現象
        other();
        sleep(1);
        // 嘗試改為 C
        System.out.println("change A->C {}" + ref.compareAndSet(prev, "C", stamp, stamp + 1));
    }
    private static void other() {
        new Thread(() -> {
            System.out.println("change A->B {}" + ref.compareAndSet(ref.getReference(), "B",
                    ref.getStamp(), ref.getStamp() + 1));
            System.out.println("更新版本為 {}" + ref.getStamp());
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            System.out.println("change B->A {}" + ref.compareAndSet(ref.getReference(), "A",
                    ref.getStamp(), ref.getStamp() + 1));
            System.out.println("更新版本為 {}" + ref.getStamp());
        }, "t2").start();
    }
}

原子數組類

  • AtomicIntegerArray(整型原子數組類)
  • AtomicLongArray(長整型原子數組類)
  • AtomicReferenceArray(引用對象原子數組類)
public class AtomicArray {

    public static void main(String[] args) {
        // 不安全數組
        test(
                ()->new int[10],
                (array)->array.length,
                (array, index) -> array[index]++,
                array-> System.out.println(Arrays.toString(array))
        );
        // 原子數組
        test(
                ()-> new AtomicIntegerArray(10),
                (array) -> array.length(),
                (array, index) -> array.getAndIncrement(index),
                array -> System.out.println(array)
        );
    }

    /**
     參數1,提供數組、可以是線程不安全數組或線程安全數組
     參數2,獲取數組長度的方法
     參數3,自增方法,回傳 array, index
     參數4,打印數組的方法
     */
    private static <T> void test(
            Supplier<T> arraySupplier,
            Function<T, Integer> lengthFun,
            BiConsumer<T, Integer> putConsumer,
            Consumer<T> printConsumer ) {
        List<Thread> ts = new ArrayList<>();
        T array = arraySupplier.get();
        int length = lengthFun.apply(array);
        for (int i = 0; i < length; i++) {
            // 每個線程對數組作 10000 次操作
            ts.add(new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    putConsumer.accept(array, j%length);
                }
            }));
        }
        // 啟動所有線程
        ts.forEach(t -> t.start());
        // 等所有線程結束
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        printConsumer.accept(array);
    }
}

字段更新器原子類

  • AtomicReferenceFieldUpdater
  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater

使用字段更新器,可以對對象的某個屬性(域 Field)進行原子操作,只能配合 volatile 修飾的字段使用,否則會出現異常:java.lang.IllegalArgumentException: Must be volatile type

public class AtomicFieldUpdaterTest {

    private volatile int field;

    public static void main(String[] args) {
        AtomicIntegerFieldUpdater fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(AtomicFieldUpdaterTest.class, "field");
        AtomicFieldUpdaterTest test5 = new AtomicFieldUpdaterTest();
        fieldUpdater.compareAndSet(test5, 0, 10);
        // 修改成功 field = 10
        System.out.println(test5.field);
        // 修改失敗 field = 10
        fieldUpdater.compareAndSet(test5, 5, 30);
        System.out.println(test5.field);
    }
}

原子累加器

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

雖然基本類型包裝類自帶原子累加操作方法,但是為了提高性能,JDK8中又提供了原子累加器,下面是效率對比測試類

public class AtomicLongAdderTest {

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            demo(() -> new LongAdder(), adder -> adder.increment());
        }
        for (int i = 0; i < 5; i++) {
            demo(() -> new AtomicLong(), adder -> adder.getAndIncrement());
        }
    }

    private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
        T adder = adderSupplier.get();
        long start = System.nanoTime();
        List<Thread> ts = new ArrayList<>();
        // 4 個線程,每人累加 50 萬
        for (int i = 0; i < 40; i++) {
            ts.add(new Thread(() -> {
                for (int j = 0; j < 500000; j++) {
                    action.accept(adder);
                }
            }));
        }
        ts.forEach(t -> t.start());
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        long end = System.nanoTime();
        System.out.println(adder + " cost:" + (end - start)/1000000);
    }
}

性能提升的原因很簡單,就是在有競爭時,設置多個累加單元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]… 最后將結果匯總。

這樣它們在累加時操作的不同的 Cell 變量,因此減少了 CAS 重試失敗,從而提高性能。

到此這篇關于Java中的原子類詳解的文章就介紹到這了,更多相關Java原子類內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 配置java.library.path加載庫文件問題

    配置java.library.path加載庫文件問題

    這篇文章主要介紹了配置java.library.path加載庫文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java語言實現基數排序代碼分享

    Java語言實現基數排序代碼分享

    這篇文章主要介紹了Java語言實現基數排序代碼分享,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • 使用Java實現RabbitMQ延時隊列

    使用Java實現RabbitMQ延時隊列

    RabbitMQ?延時隊列是指消息在發(fā)送到隊列后,并不立即被消費者消費,而是等待一段時間后再被消費者消費,本文為大家介紹了實現RabbitMQ延時隊列的Java代碼,希望對大家有所幫助
    2023-06-06
  • 喜大普奔IntelliJ IDEA 可以使用中文了

    喜大普奔IntelliJ IDEA 可以使用中文了

    IntelliJ Idea最大的亮點開始支持中文,很多英語不好的童鞋有福了,今天通過本文給大家分享如何開啟中文漢化,通過圖文步驟給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-05-05
  • Java調用C++動態(tài)庫DLL進行無圖像無實體處理

    Java調用C++動態(tài)庫DLL進行無圖像無實體處理

    這篇文章主要為大家詳細介紹了如何通過 JNI(Java Native Interface)在 Java 中調用一個用 C++ 編寫的分割算法庫,實現無圖像無實體處理,感興趣的小伙伴可以了解下
    2025-09-09
  • 淺析Spring工廠的反射和配置文件

    淺析Spring工廠的反射和配置文件

    這篇文章主要介紹了淺析Spring工廠的反射和配置文件,spring是通過反射和配置文件的方式來獲取 JavaBean 對象,需要的朋友可以參考下
    2023-04-04
  • Java Swing JList列表框的實現

    Java Swing JList列表框的實現

    這篇文章主要介紹了Java Swing JList列表框的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-12-12
  • 如何動態(tài)替換Spring容器中的Bean

    如何動態(tài)替換Spring容器中的Bean

    這篇文章主要介紹了如何動態(tài)替換Spring容器中的Bean,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java通過PDF模板填寫PDF表單

    java通過PDF模板填寫PDF表單

    這篇文章主要為大家詳細介紹了java通過PDF模板填寫PDF表單,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Java中空指針異常的幾種解決方案

    Java中空指針異常的幾種解決方案

    這篇文章主要介紹了Java中空指針異常的幾種解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01

最新評論

泌阳县| 灵山县| 黑河市| 福贡县| 连州市| 东兰县| 望谟县| 甘洛县| 东海县| 林芝县| 孙吴县| 江口县| 宁陵县| 绥中县| 油尖旺区| 顺义区| 盐池县| 黔西县| 嘉义市| 常山县| 筠连县| 云南省| 威宁| 三台县| 巨野县| 赤城县| 望谟县| 汽车| 开封市| 彭州市| 盐津县| 华蓥市| 贞丰县| 肇庆市| 吴忠市| 城固县| 深泽县| 咸宁市| 纳雍县| 南雄市| 马龙县|