Atomic原子类常用方法总结(包含四大类型)
基本介紹:
Atomic指一個操作不可中斷,即使在多線程情況下,一個操作一旦開始,就不會被其他線程干擾。如果多線程中僅需要Atomic原子類解決的事情,就不需要synchronized重量級鎖了。
原子類共四類:
基本類型:使用原子的方式更新基本類型
? a. AtomicInteger整形原子類
? b. AtomicLong長整型原子類
? c. AtomicBoolean布爾原子類
數組類型:使用原子的方式更新數組中某個元素
? a. AtomicIntegerArray:整形數組原子類
? b. AtomicLongArray:長整形數組原子類
? c. AtomicReferenceArray:引用類型數組原子類(即對應數組中存放的元素為對象形式)
引用類型:使用原子的方式更新某個對象
? a. AtomicReference:引用類型原子類
? b. AtomicStampedReference:AtomicReference的擴展版,增加了一個參數stamp標記,這里是為了解決了AtomicInteger和AtomicLong的操作會出現ABA問題。
? c. AtomicMarkableReference :與AtomicStampedReference差不多,只不過第二個參數不是用的int作為標志,而用boolean類型做標記,具體用法看后面講解。
對象的屬性修改類型:使用原子的方式更新某個類中某個字段
? a. AtomicIntegerFieldUpdater:原子更新整形字段的更新器
? b. AtomicLongFieldUpdater:原子更新長整形字段的更新器
? c. AtomicReferenceFieldUpdater:原子更新引用類型字段的更新器
使用方法:
一、基本類型原子類
由于三種類的方法基本一樣,下面就以AtomicInteger 為例:
public final int set() //設一個值
public final int get() //獲取當前的值
public final int getAndSet(int newValue)//獲取當前的值,并設置新的值
public final int getAndIncrement()//獲取當前的值,并自增
public final int getAndDecrement() //獲取當前的值,并自減
public final int getAndAdd(int delta) //獲取當前的值,并加上預期的值
boolean compareAndSet(int expect, int update) //如果當前值等于預期值,則以原子方式將該值設置為輸入值(update)
public final void lazySet(int newValue) //最終設置為newValue,使用 lazySet 設置之后可能導致其他線程在之后的一小段時間內還是可以讀到舊的值。
二、數組類型原子類
由于三種類的方法基本一樣,下面就以AtomicIntegerArray 為例:
public final int get(int i) //獲取 index=i 位置元素的值
public final int set(int i, int newValue) //為 index=i 位置元素設新值
public final int getAndSet(int i, int newValue) //返回 index=i 位置的當前的值,并將其設置為新值:newValue
public final int getAndIncrement(int i) //獲取 index=i 位置元素的值,并讓該位置的元素自增
public final int getAndDecrement(int i) //獲取 index=i 位置元素的值,并讓該位置的元素自減
public final int getAndAdd(int i, int delta) //獲取 index=i 位置元素的值,并加上預期的值
boolean compareAndSet(int i, int expect, int update) //如果index=i 位置的值等于預期值,則以原子方式將 index=i 位置的元素值設置為輸入值(update)
public final void lazySet(int i, int newValue) //最終 將index=i 位置的元素設置為newValue,使用 lazySet 設置之后可能導致其他線程在之后的一小段時間內還是可以讀到舊的值。
三、引用原子類AtomicReference
public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return this.name;}public void setName(final String name) {this.name = name;}public int getAge() {return this.age;}public void setAge(final int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';} } public class AtomicReferenceTest {public static void main(String[] args) {AtomicReference<User> u = new AtomicReference<>();User user1 = new User("金廠長",35);User user2 = new User("木易阿婆",26);User user3 = new User("快樂的小lau",22);u.set(user1);// 查看當前對象并設為新對象user2System.out.println("當前對象為:"+u.getAndSet(user2)+",并設置新對象為user2");System.out.println("當前對象為:"+u.get());System.out.println("如果當前對象為user2,就把當前對象設為user3,否則不操作");u.compareAndSet(user2,user3); //如果當前對象為user2則把當前對象設為user3System.out.println("當前對象為:"+u.get());} }四、AtomicStampedReference與AtomicMarkableReference類
ABA問題:簡單講就是多線程環境,2次讀寫中一個線程修改A->B,然后又B->A,另一個線程看到的值未改變,又繼續修改成自己的期望值。當然我們如果不關心過程,只關心結果,那么這個就是無所謂的ABA問題。
- 為了解決ABA問題,偉大的java為我們提供了AtomicMarkableReference和AtomicStampedReference類,為我們解決了問題
- AtomicStampedReference是利用版本戳的形式記錄了每次改變以后的版本號,這樣的話就不會存在ABA問題了,在這里我借鑒一下別人舉得例子
舉個通俗點的例子,你倒了一杯水放桌子上,干了點別的事,然后同事把你水喝了又給你重新倒了一杯水,你回來看水還在,拿起來就喝,如果你不管水中間被人喝過,只關心水還在,這就是ABA問題。如果你是一個講衛生講文明的小伙子,不但關心水在不在,還要在你離開的時候水被人動過沒有,因為你是程序員,所以就想起了放了張紙在旁邊,寫上初始值0,別人喝水前麻煩先做個累加才能喝水。這就是AtomicStampedReference的解決方案。
public class AtomicStampedReferenceTest {public static void main(String[] args) throws InterruptedException{final Integer init_Ref = 0, init_Stamp = 0;AtomicStampedReference<Integer> asr = new AtomicStampedReference<>(init_Ref,init_Stamp);System.out.println("init_Ref為:"+asr.getReference() + " ====== init_Stamp為:"+asr.getStamp());Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {Integer ref = asr.getReference();Integer stamp = asr.getStamp();//與前面AtomicReference的compareAndSet不同的是,增加了一個stamp標記比較,ref與stamp同時與// 當前的ref、stamp相同時才進行 + 操作System.out.println(ref + " ====== " + stamp + " ====== "+ asr.compareAndSet(ref, ref + 10, stamp, stamp + 1));}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {Integer ref = asr.getReference();// 當前的ref相同,但此時版本號不同,操作不執行返回falseSystem.out.println(ref + " ====== " + init_Stamp + " ====== "+ asr.compareAndSet(ref, ref + 10, init_Stamp, init_Stamp + 1));}});t1.start();t1.join(); //只是為了讓代碼有序執行t2.start();t2.join();System.out.println("最終的結果為:"+ asr.getReference() + " ====== " + asr.getStamp());} }
注:以上AtomicStampedReference部分摘自參考文章1, 本來看到這部分就想放棄了,但是翻到了這篇文章,講的挺清晰的,就又繼續看下去了。
可以看出,第一次ref、stamp都與輸入值相等,因此執行ref+10,和stamp+1,此時ref=10,stamp=1。第二個線程中輸入的ref與當前ref值相同,但是init_Stamp=0 與當前stamp=1 不等,因此不執行??偟膩碚f就是除了對比ref,又增加了一個stamp來判斷到底操不操作。
AtomicMarkableReference與AtomicStampedReference不同的是將int stamp改為了boolean類型的mark做標記。同樣的例子:
public class AtomicMarkableReferenceTest {public static void main(String[] args) throws InterruptedException{final Integer init_Ref = 0;final Boolean init_Mark = false;AtomicMarkableReference<Integer> amr = new AtomicMarkableReference<>(init_Ref,init_Mark);System.out.println("init_Ref為:"+amr.getReference() + " ====== init_Mark為:"+amr.isMarked());Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {Integer ref = amr.getReference();Boolean mark = amr.isMarked();//與前面AtomicReference的compareAndSet不同的是,增加了一個stamp標記比較,ref與stamp同時與// 當前的ref、stamp相同時才進行 + 操作System.out.println(ref + " ====== " + mark + " ====== "+ amr.compareAndSet(ref, ref + 10, mark, true));}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {Integer ref = amr.getReference();// 當前的ref相同,但此時版本號不同,操作不執行返回falseSystem.out.println(ref + " ====== " + init_Mark + " ====== "+ amr.compareAndSet(ref, ref + 10, init_Mark, true));}});t1.start();t1.join(); //只是為了讓代碼有序執行t2.start();t2.join();System.out.println("最終的結果為:"+ amr.getReference() + " ====== " + amr.isMarked());} }五、對象屬性修改類型
以AtomicIntegerFieldUpdater 為例介紹一下簡單使用方法:
public class User {private String name;volatile int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return this.name;}public void setName(final String name) {this.name = name;}public int getAge() {return this.age;}public void setAge(final int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';} } public class AtomicIntegerFieldUpdaterTest {public static void main(String[] args) {User user = new User("菜雞",28);AtomicIntegerFieldUpdater<User> u = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");u.addAndGet(user,5);System.out.println(user.toString());} }
這里值得注意的是:使用AtomicIntegerFieldUpdater.newUpdater修改屬性時:
具體分析可以參考:參考文章3
參考文章:
總結
以上是生活随笔為你收集整理的Atomic原子类常用方法总结(包含四大类型)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ThreadLocal两个简单示例
- 下一篇: IDEA使用自带数据库连接工具连接Mys