摘要:即使是在多個(gè)線程一起執(zhí)行的時(shí)候,一個(gè)操作一旦開始,就不會(huì)被其他線程干擾。另外是一個(gè)變量,在內(nèi)存中可見,因此可以保證任何時(shí)刻任何線程總能拿到該變量的最新值。
個(gè)人覺得這一節(jié)掌握基本的使用即可!
本節(jié)思維導(dǎo)圖:
1 Atomic 原子類介紹Atomic 翻譯成中文是原子的意思。在化學(xué)上,我們知道原子是構(gòu)成一般物質(zhì)的最小單位,在化學(xué)反應(yīng)中是不可分割的。在我們這里 Atomic 是指一個(gè)操作是不可中斷的。即使是在多個(gè)線程一起執(zhí)行的時(shí)候,一個(gè)操作一旦開始,就不會(huì)被其他線程干擾。
所以,所謂原子類說簡(jiǎn)單點(diǎn)就是具有原子/原子操作特征的類。
并發(fā)包 java.util.concurrent 的原子類都存放在java.util.concurrent.atomic下,如下圖所示。
根據(jù)操作的數(shù)據(jù)類型,可以將JUC包中的原子類分為4類
基本類型
使用原子的方式更新基本類型
AtomicInteger:整形原子類
AtomicLong:長(zhǎng)整型原子類
AtomicBoolean :布爾型原子類
數(shù)組類型
使用原子的方式更新數(shù)組里的某個(gè)元素
AtomicIntegerArray:整形數(shù)組原子類
AtomicLongArray:長(zhǎng)整形數(shù)組原子類
AtomicReferenceArray :引用類型數(shù)組原子類
引用類型
AtomicReference:引用類型原子類
AtomicStampedRerence:原子更新引用類型里的字段原子類
AtomicMarkableReference :原子更新帶有標(biāo)記位的引用類型
對(duì)象的屬性修改類型
AtomicIntegerFieldUpdater:原子更新整形字段的更新器
AtomicLongFieldUpdater:原子更新長(zhǎng)整形字段的更新器
AtomicStampedReference :原子更新帶有版本號(hào)的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于解決原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用 CAS 進(jìn)行原子更新時(shí)可能出現(xiàn)的 ABA 問題。
下面我們來詳細(xì)介紹一下這些原子類。
2 基本類型原子類 2.1 基本類型原子類介紹使用原子的方式更新基本類型
AtomicInteger:整形原子類
AtomicLong:長(zhǎng)整型原子類
AtomicBoolean :布爾型原子類
上面三個(gè)類提供的方法幾乎相同,所以我們這里以 AtomicInteger 為例子來介紹。
AtomicInteger 類常用方法
public final int get() //獲取當(dāng)前的值 public final int getAndSet(int newValue)//獲取當(dāng)前的值,并設(shè)置新的值 public final int getAndIncrement()//獲取當(dāng)前的值,并自增 public final int getAndDecrement() //獲取當(dāng)前的值,并自減 public final int getAndAdd(int delta) //獲取當(dāng)前的值,并加上預(yù)期的值 boolean compareAndSet(int expect, int update) //如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入值(update) public final void lazySet(int newValue)//最終設(shè)置為newValue,使用 lazySet 設(shè)置之后可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值。2.2 AtomicInteger 常見方法使用
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest { public static void main(String[] args) { // TODO Auto-generated method stub int temvalue = 0; AtomicInteger i = new AtomicInteger(0); temvalue = i.getAndSet(3); System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3 temvalue = i.getAndIncrement(); System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4 temvalue = i.getAndAdd(5); System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9 } }2.3 基本數(shù)據(jù)類型原子類的優(yōu)勢(shì)
通過一個(gè)簡(jiǎn)單例子帶大家看一下基本數(shù)據(jù)類型原子類的優(yōu)勢(shì)
①多線程環(huán)境不使用原子類保證線程安全(基本數(shù)據(jù)類型)
class Test { private volatile int count = 0; //若要線程安全執(zhí)行執(zhí)行count++,需要加鎖 public synchronized void increment() { count++; } public int getCount() { return count; } }
②多線程環(huán)境使用原子類保證線程安全(基本數(shù)據(jù)類型)
class Test2 { private AtomicInteger count = new AtomicInteger(); public void increment() { count.incrementAndGet(); } //使用AtomicInteger之后,不需要加鎖,也可以實(shí)現(xiàn)線程安全。 public int getCount() { return count.get(); } }2.4 AtomicInteger 線程安全原理簡(jiǎn)單分析
AtomicInteger 類的部分源碼:
// setup to use Unsafe.compareAndSwapInt for updates(更新操作時(shí)提供“比較并替換”的作用) private 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;
AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執(zhí)行效率大為提升。
CAS的原理是拿期望的值和原本的一個(gè)值作比較,如果相同則更新成新的值。UnSafe 類的 objectFieldOffset() 方法是一個(gè)本地方法,這個(gè)方法是用來拿到“原來的值”的內(nèi)存地址,返回值是 valueOffset。另外 value 是一個(gè)volatile變量,在內(nèi)存中可見,因此 JVM 可以保證任何時(shí)刻任何線程總能拿到該變量的最新值。
3 數(shù)組類型原子類 3.1 數(shù)組類型原子類介紹使用原子的方式更新數(shù)組里的某個(gè)元素
AtomicIntegerArray:整形數(shù)組原子類
AtomicLongArray:長(zhǎng)整形數(shù)組原子類
AtomicReferenceArray :引用類型數(shù)組原子類
上面三個(gè)類提供的方法幾乎相同,所以我們這里以 AtomicIntegerArray 為例子來介紹。
AtomicIntegerArray 類常用方法
public final int get(int i) //獲取 index=i 位置元素的值 public final int getAndSet(int i, int newValue)//返回 index=i 位置的當(dāng)前的值,并將其設(shè)置為新值:newValue public final int getAndIncrement(int i)//獲取 index=i 位置元素的值,并讓該位置的元素自增 public final int getAndDecrement(int i) //獲取 index=i 位置元素的值,并讓該位置的元素自減 public final int getAndAdd(int delta) //獲取 index=i 位置元素的值,并加上預(yù)期的值 boolean compareAndSet(int expect, int update) //如果輸入的數(shù)值等于預(yù)期值,則以原子方式將 index=i 位置的元素值設(shè)置為輸入值(update) public final void lazySet(int i, int newValue)//最終 將index=i 位置的元素設(shè)置為newValue,使用 lazySet 設(shè)置之后可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值。3.2 AtomicIntegerArray 常見方法使用
import java.util.concurrent.atomic.AtomicIntegerArray; public class AtomicIntegerArrayTest { public static void main(String[] args) { // TODO Auto-generated method stub 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); } }4 引用類型原子類 4.1 引用類型原子類介紹
基本類型原子類只能更新一個(gè)變量,如果需要原子更新多個(gè)變量,需要使用 引用類型原子類。
AtomicReference:引用類型原子類
AtomicStampedRerence:原子更新引用類型里的字段原子類
AtomicMarkableReference :原子更新帶有標(biāo)記位的引用類型
上面三個(gè)類提供的方法幾乎相同,所以我們這里以 AtomicReference 為例子來介紹。
4.2 AtomicReference 類使用示例import java.util.concurrent.atomic.AtomicReference; public class AtomicReferenceTest { public static void main(String[] args) { AtomicReferencear = new AtomicReference (); Person person = new Person("SnailClimb", 22); ar.set(person); Person updatePerson = new Person("Daisy", 20); ar.compareAndSet(person, updatePerson); System.out.println(ar.get().getName()); System.out.println(ar.get().getAge()); } } class Person { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
上述代碼首先創(chuàng)建了一個(gè) Person 對(duì)象,然后把 Person 對(duì)象設(shè)置進(jìn) AtomicReference 對(duì)象中,然后調(diào)用 compareAndSet 方法,該方法就是通過通過 CAS 操作設(shè)置 ar。如果 ar 的值為 person 的話,則將其設(shè)置為 updatePerson。實(shí)現(xiàn)原理與 AtomicInteger 類中的 compareAndSet 方法相同。運(yùn)行上面的代碼后的輸出結(jié)果如下:
Daisy 205 對(duì)象的屬性修改類型原子類 5.1 對(duì)象的屬性修改類型原子類介紹
如果需要原子更新某個(gè)類里的某個(gè)字段時(shí),需要用到對(duì)象的屬性修改類型原子類。
AtomicIntegerFieldUpdater:原子更新整形字段的更新器
AtomicLongFieldUpdater:原子更新長(zhǎng)整形字段的更新器
AtomicStampedReference :原子更新帶有版本號(hào)的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于解決原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用 CAS 進(jìn)行原子更新時(shí)可能出現(xiàn)的 ABA 問題。
要想原子地更新對(duì)象的屬性需要兩步。第一步,因?yàn)閷?duì)象的屬性修改類型原子類都是抽象類,所以每次使用都必須使用靜態(tài)方法 newUpdater()創(chuàng)建一個(gè)更新器,并且需要設(shè)置想要更新的類和屬性。第二步,更新的對(duì)象屬性必須使用 public volatile 修飾符。
上面三個(gè)類提供的方法幾乎相同,所以我們這里以 AtomicIntegerFieldUpdater為例子來介紹。
5.2 AtomicIntegerFieldUpdater 類使用示例import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicIntegerFieldUpdaterTest { public static void main(String[] args) { AtomicIntegerFieldUpdatera = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); User user = new User("Java", 22); System.out.println(a.getAndIncrement(user));// 22 System.out.println(a.get(user));// 23 } } class User { private String name; public volatile int age; public User(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
輸出結(jié)果:
22 23
【強(qiáng)烈推薦】最后,給大家推薦一下阿里云最新雙11福利活動(dòng)(僅限阿里云新用戶購買,老用戶拉新用戶可以獲得返現(xiàn)紅包,后續(xù)有機(jī)會(huì)平分百萬紅包),優(yōu)惠力度非常非常非常大,另外加入拼團(tuán),后續(xù)還有機(jī)會(huì)平分100w紅包!目前我的戰(zhàn)隊(duì)已經(jīng)有50多位新人了,現(xiàn)在是折上5折了也就是1折購買,已經(jīng)達(dá)到了最低折扣?。。。。?!。 劃重點(diǎn)了: 1核2G云服務(wù)器1年僅需99.5元?。?!1核2G云服務(wù)器3年僅需298.50元?。?!一個(gè)月僅需8.2元 該折扣僅限新人!這是我的團(tuán)隊(duì)拼團(tuán)地址:https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.hf47liqn !
另外,老用戶可以加入我的戰(zhàn)隊(duì)幫忙拉新,拉新你可以獲得什么福利呢?①即時(shí)紅包,即拆即用(最低紅包10元,最高1111元);②瓜分百萬紅包的機(jī)會(huì)(目前我的戰(zhàn)隊(duì)已經(jīng)有29位新人,所以沖進(jìn)前100的可能性非常大!沖進(jìn)之后即可瓜分百萬紅包?。鄯惮F(xiàn)獎(jiǎng)勵(lì),如果你邀請(qǐng)了新人你會(huì)獲得返現(xiàn)獎(jiǎng)勵(lì),返現(xiàn)獎(jiǎng)勵(lì)直接到你的賬戶?。ㄎ蚁M业膱F(tuán)隊(duì)最后能夠沖進(jìn)前100,別的不多說?。。≌\信?。?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71965.html
摘要:整個(gè)包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見的多線程設(shè)計(jì)模式,設(shè)計(jì)了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對(duì)等進(jìn)行補(bǔ)充增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:線程的這種交叉操作會(huì)導(dǎo)致線程不安全。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段。如果聲明一個(gè)域?yàn)橐恍┣闆r就可以確保多線程訪問到的變量是最新的。并發(fā)要求一個(gè)線程對(duì)對(duì)象進(jìn)行了操作,對(duì)象發(fā)生了變化,這種變化應(yīng)該對(duì)其他線程是可見的。 雖是讀書筆記,但是如轉(zhuǎn)載請(qǐng)注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 一個(gè)問題: ...
摘要:今天給大家總結(jié)一下,面試中出鏡率很高的幾個(gè)多線程面試題,希望對(duì)大家學(xué)習(xí)和面試都能有所幫助。指令重排在單線程環(huán)境下不會(huì)出先問題,但是在多線程環(huán)境下會(huì)導(dǎo)致一個(gè)線程獲得還沒有初始化的實(shí)例。使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運(yùn)行。 下面最近發(fā)的一些并發(fā)編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發(fā)編程問題就沒有那么頭疼了。今天給大家總結(jié)一下,面試中出鏡率很高的幾個(gè)多線...
摘要:方法由兩個(gè)參數(shù),表示期望的值,表示要給設(shè)置的新值。操作包含三個(gè)操作數(shù)內(nèi)存位置預(yù)期原值和新值。如果處的值尚未同時(shí)更改,則操作成功。中就使用了這樣的操作。上面操作還有一點(diǎn)是將事務(wù)范圍縮小了,也提升了系統(tǒng)并發(fā)處理的性能。 這是java高并發(fā)系列第21篇文章。 本文主要內(nèi)容 從網(wǎng)站計(jì)數(shù)器實(shí)現(xiàn)中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數(shù)據(jù)庫...
摘要:本身不直接支持指針的操作,所以這也是該類命名為的原因之一。中的許多方法,內(nèi)部其實(shí)都是類在操作。 showImg(https://segmentfault.com/img/remote/1460000016012251); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一、Unsafe簡(jiǎn)介 在正式的開講 juc-atomic框架系列之前,有...
閱讀 2572·2021-09-09 09:33
閱讀 2939·2019-08-30 15:56
閱讀 3209·2019-08-30 14:21
閱讀 964·2019-08-30 13:01
閱讀 957·2019-08-26 18:27
閱讀 3660·2019-08-26 13:47
閱讀 3533·2019-08-26 10:26
閱讀 1658·2019-08-23 18:38