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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java原子类

發(fā)布時(shí)間:2025/3/15 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java原子类 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們知道Java提供了兩種方式來處理線程安全的問題。第一種是互斥同步(悲觀鎖),第二種是采用非阻塞式同步(樂觀鎖)。雖然以上兩種方案都能解決線程安全的問題。但是在JDK1.5開始,就提供了java.util.concurrent.atomic包,這個(gè)包中的原子操作類提供了更為簡單高效、線程安全的方式來更新一個(gè)變量的值。例如AtomicBoolean、AtomicLong、AtomicInteger等。(這里提到的Atomic系列類原理都是CAS操作)

一、原子類作用及類型

作用:由Atomic可知,這些類提供的操作是不可中斷的。即使是在多個(gè)線程一起執(zhí)行的時(shí)候,一個(gè)操作一旦開始,就不會被其他線程干擾。

類型:

  • 基本類型:使用原子的方式更新基本類型
  • AtomicInteger:整形原子類 AtomicLong:長整型原子類 AtomicBoolean:布爾型原子類
  • 數(shù)組類型:使用原子的方式更新數(shù)組里的某個(gè)元素
  • AtomicIntegerArray:整形數(shù)組原子類 AtomicLongArray:長整形數(shù)組原子類 AtomicReferenceArray:引用類型數(shù)組原子類
  • 引用類型
  • AtomicReference:引用類型原子類 AtomicStampedRerence:原子更新引用類型里的字段原子類 AtomicMarkableReference :原子更新帶有標(biāo)記位的引用類型
  • 對象的屬性修改類型
  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器 AtomicLongFieldUpdater:原子更新長整形字段的更新器 AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于解決原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號,可以解決使用 CAS 進(jìn)行原子更新時(shí)可能出現(xiàn)的 ABA 問題。

    二、基本類型的線程同步綜合例子(static、原子類、synchronized修飾的類、volatile)

    public class AtomicClassTest {public static int count = 0;//synchronized修飾的類public static Counter counter = new Counter();public static AtomicInteger atomicInteger = new AtomicInteger(0);volatile public static int countVolatile = 0;public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread() {@Overridepublic void run() {for (int j = 0; j < 1000; j++) {count++;counter.increment();atomicInteger.getAndIncrement();countVolatile++;}}}.start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("static count: " + count);System.out.println("Counter(synchronized修飾的類): " + counter.getValue());System.out.println("AtomicInteger(原子類): " + atomicInteger.intValue());System.out.println("countVolatile(volatitle修飾 ): " + countVolatile);}} class Counter {private int value;public synchronized int getValue() {return value;}public synchronized int increment() {return ++value;}public synchronized int decrement() {return --value;} }


    通過上面的例子說明,要解決自增操作在多線程環(huán)境下線程不安全的問題,可以選擇使用Java提供的原子類,或者使用synchronized同步方法。而通過Volatile關(guān)鍵字,并不能解決非原子操作的線程安全性
    順帶說一下Java中的自增原理:自增并不是一個(gè)原子操作(這是一個(gè)“讀取 - 修改 - 寫入”的操作序列,并且其結(jié)果狀態(tài)依賴于之前的狀態(tài)),這也是volatile在上面例子不起作用的原因。(因?yàn)関olatile只是保證操作都是在主存中進(jìn)行,可是并不保證線程的互斥和原子性,而這里的count++操作是先 int temp=count;然后 count=count+1;然后寫入主存。這里是有3個(gè)操作。當(dāng)A線程進(jìn)行int temp=count操作的時(shí)候,此時(shí)切換到另一個(gè)線程也進(jìn)行這個(gè)操作,那么從主存中讀取出來的值是一樣的,然后在自己的線程內(nèi)部進(jìn)行操作,那么進(jìn)行到最后的寫入的時(shí)候,寫入的值就是一樣的了。所以由于i++有三個(gè)操作,而volatile只能保證內(nèi)存可見性。導(dǎo)致了線程安全問題的出現(xiàn)。)(不要將volatile用在getAndOperate場合(這種場合不原子,需要再加鎖),僅僅set或者get的場景是適合volatile的)

    三、數(shù)組類型原子類

    對于數(shù)組類型的原子類,在Java中,主要通過原子的方式更新數(shù)組里面的某個(gè)元素,以AtomicIntegerArray為例,因?yàn)槠鋬?nèi)部原理都是循環(huán)CAS操作,所以我們這里就描述其使用方式,具體代碼如下:

    public class AtomicArray {private int[] value = new int[]{0, 1, 2};private AtomicIntegerArray mAtomicIntegerArray = new AtomicIntegerArray(value);private void doAdd() {for (int i = 0; i < 5; i++) {int value = mAtomicIntegerArray.addAndGet(0, 1);System.out.println(Thread.currentThread().getName() + "--->" + value);}}public static void main(String[] args) {AtomicArray demo = new AtomicArray();new Thread(demo::doAdd, "線程1").start();new Thread(demo::doAdd, "線程2").start();} }

    四、引用類型原子類

    我們知道CAS雖然很高效,但是它也存在三大問題,這里也簡單說一下:

  • ABA問題(JDK從1.5開始提供了AtomicStampedReference類來解決ABA問題)(關(guān)于ABA可能引起的問題)
  • 循環(huán)時(shí)間長開銷大
    CAS操作如果長時(shí)間不成功,會導(dǎo)致其一直自旋,給CPU帶來非常大的開銷。
  • 只能保證一個(gè)共享變量的原子操作(AtomicReference類解決)
  • 使用AtomicReference解決多變量共享問題:

    public class CAS_Multiple {Person mPerson = new Person("紅紅", 1);private AtomicReference<Person> mAtomicReference = new AtomicReference<>(mPerson);private class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}}private void updatePersonInfo(String name, int age) throws Exception {System.out.println(Thread.currentThread().getName() + "更新前--->" + mAtomicReference.get().name + "---->" + mAtomicReference.get().age);mAtomicReference.getAndUpdate(person -> new Person(name, age));}public static void main(String[] args) throws InterruptedException {CAS_Multiple demo = new CAS_Multiple();new Thread(() -> {try {demo.updatePersonInfo("藍(lán)藍(lán)", 2);} catch (Exception e) {e.printStackTrace();}}, "線程1").start();Thread.sleep(1000);System.out.println("暫停一秒--->" + demo.mAtomicReference.get().name + "---->" + demo.mAtomicReference.get().age);System.out.println("更新后---->" + demo.mAtomicReference.get().name + "---->" + demo.mAtomicReference.get().age);} } //輸出結(jié)果 線程1更新前--->紅紅---->1 暫停一秒--->藍(lán)藍(lán)---->2 更新后---->藍(lán)藍(lán)---->2

    AtomicStampedReference解決ABA問題:

    public class ABA_AtomicStampedReference {Person mPerson = new Person("紅紅", 1);private AtomicStampedReference<Person> mAtomicReference = new AtomicStampedReference<>(mPerson, 1);private class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}}/*** 更新信息* @param name 名稱* @param age 年齡* @param oldStamp CAS操作比較的舊的版本* @param newStamp 希望更新后的版本*/private void updatePersonInfo(String name, int age, int oldStamp, int newStamp) {System.out.println(Thread.currentThread().getName() + "更新前--->" + mAtomicReference.getReference().name + "---->" + mAtomicReference.getReference().age);mAtomicReference.compareAndSet(mPerson, new Person(name, age), oldStamp, newStamp);}public static void main(String[] args) throws Exception {ABA_AtomicStampedReference demo = new ABA_AtomicStampedReference();new Thread(() -> demo.updatePersonInfo("藍(lán)藍(lán)", 2, 1, 2), "線程1").start();Thread.sleep(1000);System.out.println("暫停一秒--->" + demo.mAtomicReference.getReference().name + "---->" + demo.mAtomicReference.getReference().age);new Thread(() -> demo.updatePersonInfo("花花", 3, 1, 3), "線程2").start();Thread.sleep(1000);System.out.println("更新后---->" + demo.mAtomicReference.getReference().name + "---->" + demo.mAtomicReference.getReference().age);} } //結(jié)果 線程1更新前--->紅紅---->1 暫停一秒--->藍(lán)藍(lán)---->2 線程2更新前--->藍(lán)藍(lán)---->2 更新后---->藍(lán)藍(lán)---->2

    在上述代碼中,我們使用AtomicStampedReference類,其中在使用該類的時(shí)候,需要傳入一個(gè)類似于版本(你也可以叫做郵戳,時(shí)間戳等,隨你喜歡)的int類型的屬性。在Main方法中我們分別創(chuàng)建了2個(gè)線程來進(jìn)行CAS操作,其中線程1想做的操作是將版本為1的mPerson(“紅紅”,1)修改為版本為2的Person(“藍(lán)藍(lán),2”)。當(dāng)線程1執(zhí)行完畢后,緊接著線程2開始執(zhí)行,線程2想做的操作是將版本為1的mPerson(“紅紅”,1)修改為版本3的Person(“花花”,3)。從程序輸出結(jié)果可以看出,線程2的操作是沒有執(zhí)行的。也就驗(yàn)證了AtomicStampedReference確實(shí)解決了ABA的問題。

    五、字段類型原子類

    如果需要更新某個(gè)類中的某個(gè)字段,在Actomic系列中,Java提供了3個(gè)類來實(shí)現(xiàn),原理都大同小異,這里我們以AtomicIntegerFieldUpdate類來講解,具體代碼如下:

    public class Test_AtomicIntegerFieldUpdate {Person mPerson = new Person("紅紅", 1);private AtomicIntegerFieldUpdater<Person> mFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");private class Person {String name;volatile int age;//使用volatile修飾Person(String name, int age) {this.name = name;this.age = age;}}/*** 更新信息* @param age 年齡*/private void updatePersonInfo(int age) {System.out.println("更新前--->" + mPerson.age);mFieldUpdater.addAndGet(mPerson, age);}private int getUpdateInfo() {return mFieldUpdater.get(mPerson);}public static void main(String[] args) throws Exception {Test_AtomicIntegerFieldUpdate demo = new Test_AtomicIntegerFieldUpdate();new Thread(() -> demo.updatePersonInfo(12), "線程1").start();Thread.sleep(1000);System.out.println("更新后--->" + demo.getUpdateInfo());} } //結(jié)果 更新前--->1 更新后--->13

    這里對AtomicIntegerFieldUpdate不在進(jìn)行過多的描述,大家需要主要的是在使用字段類型原子類的時(shí)候,需要進(jìn)行更新的字段,需要通過volatile來修飾。

    六、總結(jié)

  • Atomic系列類為我們提供了簡單高效、線程安全的方式來更新一個(gè)變量的值或一個(gè)引用的值。
  • Atomic為處理多個(gè)變量原子更新的問題,為我們提供了AtomicReference類,為了解決ABA問題提供了AtomicStampedReference。在實(shí)際使用中,根據(jù)代碼情況來使用不同Atomic的系列類。
  • 在使用字段類型原子類的時(shí)候,需要將需要更新的字段,通過volatile來修飾。
    本文轉(zhuǎn)載
  • 總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。