並發之原子性、有序性、可見性

2021-01-13 java技術閱讀

java並發程序都是基於多線程,作業系統為了充分利用CPU的資源,將CPU分成若干個時間片,線程會被作業系統調度進行任務切換,達到最大限度地利用CPU空閒時間。

多線程編程雖然可以最大限度地利用CPU,但也突顯了三個問題:原子性問題,可見性問題,有序性問題。

原子性atomic

原子性指的是一個或者多個操作在CPU執行過程中不被中斷的特性。即要不全部執行完成,要不不執行。

請看上面的代碼塊,我們來分析下操作2:

指令1:把變量a從內存加載到CPU寄存器中;指令2:在寄存器中執行+1操作;指令3:將結果寫入內存。通過上面的三步指令操作,完成了「a++」的操作。

假設線程A在完成「指令1」後切換到線程B,線程B完成操作2後切換到線程A,A繼續完成指令2和指令3的步驟,我們發現兩個線程都執行了「a++」的操作,但是結果卻不是我們期望的2,而是1。

這就是原子性的問題,為了解決這個問題,我們需要用到互斥來使操作變為原子性。方法為:使用鎖或者使用原子函數。

可見性volatile

可見性指的是當一個線程修改了共享變量後,其他線程能夠立即得到修改的值。

同樣是上圖中的代碼塊,我們做下規定:操作1和操作2是線程A執行的代碼;操作3是線程B執行的代碼。

假設執行線程A的是CPU1,執行線程B的是CPU2。當線程A執行「a++」這句的時候,會先把a的初始值加載到CPU1的寄存器中,然後自增1,那麼CPU1中的寄存器中a的值變為1,卻沒有立即寫入內存中。

此時線程B執行操作3「b=a」代碼,它會去內存中讀取a的值,此時內存中i的值還是0,那麼完成操作3後,b的值為0,而不是1。

這就是可見性的問題,線程A對變量a修改後,線程B沒有立即看到線程A修改的值。為了解決這個問題,我們設置變量為volatile可見性。volatile的特殊規則保證了新值能立即同步到內存中,已經沒使用前立即從內存刷新數據。

有序性

有序性指的是程序執行的順序按照代碼的先後順序執行。

一般來說,處理器為了提高程序的的運行效率,可能會對輸入代碼進行優化,它不保證程序中各個操作語句的執行先後數據同代碼中的順序一致,但是它會保證程序最終執行結果和代碼順序執行結果的一致性。

比如上圖所示,兩種順序都有可能發生,不過不會影響代碼執行結果的一致性,因為重排序會考慮指令之間的依賴,所以不會出現操作3,在操作4後面的情況。

雖然重排序不會影響單個線程內程序的執行結果,但是多線程呢?

上圖代碼塊中,由於操作1和操作2沒有數據依賴,因此可能會被重排序。假如發生重排序,線程A執行過程中操作2發生在操作1之前,而此時線程B以為初始化工作完成,那麼就會跳出while循環,去執行doSomething(context)方法,而此時context並沒有被初始化,就會導致程序出錯。

為了解決這個問題,我們可以使用synchronized和volatile來保證多線程之間操作的有序性。volatile關鍵字會禁止指令重排;synchronized關鍵字保證同一時刻只允許一條線程操作。

相關焦點

  • 深入分析volatile是如何實現可見性和有序性的
    並發編程中常常繞不開原子性、可見性與有序性的討論。先來看看什麼是原子性?什麼是原子性原子(atomic)本意是不能被進一步分割的最小粒子,而原子操作意為不可被中斷的一個或者一系列操作。volatile是不能保證原子性,但是在特定場景就是在32位處理器裡,對double和long型的變量的讀寫操作加了volatile修飾可以保證原子性。
  • Java中long和double的非原子性你了解嗎?
    換句話說,讀取一個非long或double類型的變量,可以保證返回的值是某個線程保存在該變量中的,即時多個線程在沒有同步的情況下並發地修改這個變量也是如此。其實本來很簡單的一句話我反覆讀了幾遍還沒明白是啥意思,直到後來網上查了一下才恍然大悟。三個特性JMM的三大特性:可見性、有序性、原子性。
  • Java高級程式設計師須知的並發編程知識
    並發編程簡介並發編程式Java語言的重要特性之一,當然也是最難以掌握的內容。編寫可靠的並發程序是一項不小的挑戰。但是,作為程式設計師的我們,要變得更有價值,就需要啃一些硬骨頭了。因此,理解並發編程的基礎理論和編程實踐,讓自己變得更值錢吧。
  • 機率大的 Redis 面試題(含答案)|內存|key|原子性|哈希|redis_網易...
    緩存穿透與緩存擊穿的區別  緩存擊穿:是指一個key非常熱點,在不停的扛著大並發,大並發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大並發就穿破緩存,直接請求數據。set,hash支持事務,操作都是原子性,所謂的原子性就是對數據的更改要麼全部執行,要麼全部不執行豐富的特性:可用於緩存,消息,按key設置過期時間,過期後將會自動刪除如何解決redis的並發競爭key問題  同時有多個子系統去set一個key。
  • 高並發場景下如何使用ThreadLocalRandom替代Random?
    本博客系列是學習並發編程過程中的記錄總結。隨機數隨機數在科學研究與工程實際中有著極其重要的應用!簡單來說,隨機數就是一個數列,這個數列可能滿足一定的概率分布,又獲取其滿足的分布並不為我們所知。數學方法產生隨機數應該稱之為「偽隨機數」,只有使用物理方法才能得到真正的隨機數!
  • 當我們在思考「可見性」時,我們在思考什麼?
    學者們則擁有更加「體面」的說法:網際網路提供了前所未有的「可見性」。正如傳播學者Jeffrey Treem所說,歡迎來到傳播可見性的時代。 01. 可見性為何重要?如同絕大多數學術概念一樣,對「可見性」的思考,也並非發端於網際網路。
  • 高質量的論文摘要,是如何提升論文可見性的?
    在我們的論文寫作中,如何有效地利用論文摘要提升文章的可見性?我們將從以下幾個方面詳細介紹。 01 用搜尋引擎優化摘要 搜尋引擎的優化是提升論文可見性的捷徑。
  • 病毒DNA在受限空間中的多區域有序性研究獲進展
    染色質的結構也會出現多區域有序性,其中蛋白質與DNA之間複雜的相互作用能夠驅動DNA的分離。目前這一工作表明即使在沒有特定結構束縛的情況下,DNA的多區域有序性仍可以出現在更簡單與更微小的系統中。當然,如果要精確地預測DNA在病毒中的堆積形態,則需要進一步考慮DNA與病毒外殼之間、以及DNA分子內部所具有的特定相互作用。
  • 進展|病毒DNA在受限空間中的多區域有序性
    染色質的結構也會出現多區域有序性,其中蛋白質與DNA之間複雜的相互作用能夠驅動DNA的分離。目前這一工作表明即使在沒有特定結構束縛的情況下,DNA的多區域有序性仍可以出現在更簡單與更微小的系統中。當然,如果要精確地預測DNA在病毒中的堆積形態,則需要進一步考慮DNA與病毒外殼之間、以及DNA分子內部所具有的特定相互作用。
  • 並發與並行的區別
    Differences between concurrency vs. parallelism翻譯成中文:並發是兩個任務可以在重疊的時間段內啟動,運行和完成。並行是任務在同一時間運行,例如,在多核處理器上。並發是獨立執行過程的組合,而並行是同時執行(可能相關的)計算。並發是一次處理很多事情,並行是同時做很多事情。
  • 光纖並發通信的腦洞
    光纖並發通信的腦洞,天熱的難受,待在家裡閒的發悶,剪了一段廢舊的光纖,研究研究突然腦子裡有個靈感,通過光纖進行鏡像傳播數據的腦洞,因為自己文化水平實在有限,不懂其中的理論原理,但用雷射筆給光纖打光,發現兩端的影像竟然是一樣(表面一樣)的!
  • RESTAPI與Spring框架下的並發控制
    您有沒有想過並發控制應該如何反映在我們的API中?當兩個用戶同時更新相同的記錄時會發生什麼?我們會發送錯誤信息嗎?我們將使用什麼HTTP響應代碼?我們將附加哪些HTTP頭?本文的目的是全面說明如何對RESTAPI建模,使其支持資源的並發控制,並利用HTTP協議的特性。我們還將在Spring框架的幫助下實現此解決方案。