摘要:前言今天的筆記來了解一下原子操作以及中如何實現(xiàn)原子操作。概念原子本意是不能被進一步分割的最小粒子,而原子操作意為不可被中斷的一個或一系列操作。處理器實現(xiàn)原子操作處理器會保證基本內(nèi)存操作的原子性。
前言
今天的筆記來了解一下原子操作以及Java中如何實現(xiàn)原子操作。
概念原子(atomic)本意是“不能被進一步分割的最小粒子”,而原子操作(atomic operation)意為“不可被中斷的一個或一系列操作”。
處理器實現(xiàn)原子操作處理器會保證基本內(nèi)存操作的原子性。處理器保證從系統(tǒng)內(nèi)存中讀取或者寫入一個字節(jié)是原子的,意思是當(dāng)一個處理器讀取一個字節(jié)時,其他處理器不能訪問這個字節(jié)的內(nèi)存地址。最新的處理器能自動保證單處理器進行16/32/64位的操作是原子的,并且提供總線鎖定和緩存鎖定兩個機制來保證復(fù)雜內(nèi)存操作的原子性。
如果有多個處理器同時對共享變量進行操作,那么共享變量就會被多個處理器同時操作,這樣的話,讀改寫操作就不是原子的。
比如i=1,i++,兩個處理器同時進行操作,最后的結(jié)果,可能是3,也可能是3.
原因可能是多個處理器同時從各自的緩存中讀取變量i,分別進行加1操作,然后分別寫入系統(tǒng)內(nèi)存。
處理器使用總線鎖來解決這個問題。當(dāng)處理器發(fā)出LOCK#信號時,其他處理器的請求會被阻塞主,該處理器可以獨占共享內(nèi)存。
鎖總線開銷還是很大的,鎖住了CPU和內(nèi)存之間的通信。
因為頻繁使用的內(nèi)存會緩存在處理器的L1、L2、L3高速緩存中,原子操作可以在緩存內(nèi)部完成,同時通過緩存一致性協(xié)議,當(dāng)A處理器修改緩存中的i時,其他處理器不能同時緩存i,即會使得其他處理器中對于共享變量的緩存失效。
這段還不是特別明白,感覺得重新翻一下操作系統(tǒng),有知道的網(wǎng)友可以留言補充一下。
Java可以使用鎖,實現(xiàn)一段代碼的原子操作。但這樣開銷比較大,會引起頻繁的上下文切換。
另外一種方式就是使用CAS操作(比較交換)。
CAS算法的過程是比較簡單的。它會包含三個參數(shù)(V,E,N))。V表示要更新的變量,E表示預(yù)期值,N值。當(dāng)且僅當(dāng)V等于E值時,才會將V的值設(shè)為N,如果V值和E值不同,說明已經(jīng)有其他線程做了更新,則當(dāng)前線程什么都不做。
當(dāng)多個線程同時使用CAS對變量進行操作時,只有一個會勝出并成功更新,其余會失敗。失敗的線程不會被掛起。
Java中對于基本類型的包裝類都有對應(yīng)的原子操作實現(xiàn),比如
AtomicBoolean
AtomicInteger
等
如果拿AtomicInteger為例子,其中的incrementAndGet的實現(xiàn)如下所示,是直接調(diào)用了Unsafe類的方法:
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
其中var1是傳入對象的引用,var2是字段到對象頭部的偏移量,方便快速定位,var5是當(dāng)前值,var5+var4就是期望值,var4傳入的是1。
如果是引用類型的話,可以使用AtomicReference。
CAS操作雖好,但它會遇到ABA問題,即一個變量先是A,后來變成了B,在比較時又變回了A,但CAS操作無法感知到這種情況,如果說我們是否可以修改當(dāng)前值,不僅取決于當(dāng)前值,還取決于它的變化,那么原有的CAS操作就無能為力了,因為它感知不到。
貼心的JDK為我們提供了AtomicStampedReference,它在對象內(nèi)部維護了時間戳,當(dāng)更新數(shù)據(jù)時,不僅要更新數(shù)據(jù),還要更新時間戳。當(dāng)AtomicStampedReference設(shè)置對象值時,對象值以及時間戳都必須滿足期望值,寫入才會成功。
如果是數(shù)組類型的話,JDK提供了AtomicIntegerArray等數(shù)組類型的原子類。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/70559.html
摘要:目前看的部分主要是這個關(guān)鍵字。語言提供了,保證了所有線程能看到共享變量最新的值。前綴的指令在多核處理器下會做兩件事情將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。 這一章節(jié)的話,主要是講一下在并發(fā)操作中常見的volatile、synchronized以及原子操作的相關(guān)知識。 目前看的部分主要是volatile這個關(guān)鍵字。 volatile 根據(jù)Java語言規(guī)范第3版中對volatile的定義...
摘要:在之前的文章中學(xué)習(xí)了關(guān)鍵字,可以保證變量在線程間的可見性,但他不能真正的保證線程安全。線程執(zhí)行到指令時,將會嘗試獲取對象所對應(yīng)的的所有權(quán),即嘗試獲得對象的鎖。從可見性上來說,線程通過持有鎖的方式獲取變量的最新值。 在之前的文章中學(xué)習(xí)了volatile關(guān)鍵字,volatile可以保證變量在線程間的可見性,但他不能真正的保證線程安全。 /** * @author cenkailun *...
摘要:前言并發(fā)編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。盡可能降低上下文切換的次數(shù),有助于提高并發(fā)效率。死鎖并發(fā)編程中的另一挑戰(zhàn)是死鎖,會造成系統(tǒng)功能不可用。 前言 并發(fā)編程的目的是讓程序跑的更快,但并不是啟動更多的線程,這個程序就跑的更快。有以下幾種挑戰(zhàn)。 挑戰(zhàn)及方案 上下文切換 單核CPU上執(zhí)行多線程任務(wù),通過給每個線程分配CPU時間片的方式來實現(xiàn)這個機制。...
摘要:開頭正式開啟我入職的里程,現(xiàn)在已是工作了一個星期了,這個星期算是我入職的過渡期,算是知道了學(xué)校生活和工作的差距了,總之,盡快習(xí)慣這種生活吧。當(dāng)時是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎(chǔ)上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...
摘要:發(fā)布的對象內(nèi)部狀態(tài)可能會破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對象的固有屬性之一??勺儗ο蟊仨毻ㄟ^安全方式來發(fā)布,并且必須是線程安全的或者有某個鎖保護起來。 線程的優(yōu)缺點 線程是系統(tǒng)調(diào)度的基本單位。線程如果使用得當(dāng),可以有效地降低程序的開發(fā)和維護等成本,同時提升復(fù)雜應(yīng)用程序的性能。多線程程序可以通過提高處理器資源的利用率來提升系統(tǒng)的吞吐率。與此同時,在線程的使用...
閱讀 3428·2021-11-19 09:40
閱讀 1314·2021-10-11 11:07
閱讀 4844·2021-09-22 15:07
閱讀 2890·2021-09-02 15:15
閱讀 1964·2019-08-30 15:55
閱讀 539·2019-08-30 15:43
閱讀 883·2019-08-30 11:13
閱讀 1449·2019-08-29 15:36