您好,登錄后才能下訂單哦!
本篇內容介紹了“高級并發編程系列之什么是原子類”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
相信作為資深程序員的你,對于AtomicInteger這樣的類,即以Atomic開始的類一定不會感到陌生。我們在翻看很多框架源碼、或者第三方組件都會經常看到它們,如影隨形。
那么問題來了,像Atomicxxx這樣的類,到底是什么意思呢?從字面意思比較好理解,Atomic即原子性,那么Atomicxxx即原子類。講到這里,你一定還記得我們說過線程安全的三個基本要素,我們一起來回顧一下:可見性、原子性、有序性。原子類的原子性,講的就是這個原子性,于是你可以先記住一個結論:原子類,它是線程安全的類。
到這里有朋友可能會提出質疑:你說線程安全,就線程安全嗎?我不服,你沒有講清楚。我不聽,我不聽......好吧,看官們莫急,且聽我一步一步分析,娓娓道來,話說......
#考考你: 1.你真的理解原子類的核心思想嗎 2.你在你的項目中,有直接用到過原子類嗎
案例描述:
定義一個普通的int型變量value,初始值為:0
開啟兩個線程thread_1,thread_2并行執行value++操作
每個線程執行 5000次,預期執行結果: 2 * 5000 = 10000次
通過觀察最終執行結果,是否等于預期10000次
結果不相等,說明線程不安全,原因是:value++操作不是一個原子性操作
package com.anan.edu.common.newthread.atomic; /** * 普通 int變量 ++ 操作,非原子性,線程不安全 * * @author ThinkPad * @version 1.0 * @date 2020/11/29 8:27 */ public class CommonIntDemo { /** * 普通成員變量 */ private int value = 0; public void addValue(){ value++; } public static void main(String[] args) throws InterruptedException { // 1.創建CommonIntDemo對象 CommonIntDemo demo = new CommonIntDemo(); // 2.創建2兩個線程,每個線程調用方法addValue 5000次 // 預期value值結果等于:2 * 5000 = 10000 int loopEnd = 5000; Thread thread_1 = new Thread(() -> { for(int i = 0; i < loopEnd; i++){ demo.addValue(); } }, "thread_1"); Thread thread_2 = new Thread(() -> { for(int i = 0; i < loopEnd; i++){ demo.addValue(); } }, "thread_2"); // 3.啟動執行線程 thread_1.start(); thread_2.start(); // 4.主線程等待子線程執行完成,打印value值 thread_1.join(); thread_2.join(); System.out.println("int型成員變量value最終結果:" + demo.value); } }
執行結果分析:
案例描述:
定義一個AtomicInteger變量value,初始值為:0
開啟兩個線程thread_1,thread_2并行執行value.incrementAndGet()操作
每個線程執行 5000次,預期執行結果: 2 * 5000 = 10000次
通過觀察最終執行結果,是否等于預期10000次
結果相等,說明線程安全,原因是:原子類同時滿足了可見性、與原子性
package com.anan.edu.common.newthread.atomic; import java.util.concurrent.atomic.AtomicInteger; /** * 原子類AtomicInteger,實現自增操作,線程安全 * * @author ThinkPad * @version 1.0 * @date 2020/11/29 8:27 */ public class AtomicIntegerDemo { /** * AtomicInteger成員變量 */ private AtomicInteger value = new AtomicInteger(0); public void addValue(){ value.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { // 1.創建AtomicIntegerDemo對象 AtomicIntegerDemo demo = new AtomicIntegerDemo(); // 2.創建2兩個線程,每個線程調用方法addValue 5000次 // 預期value值結果等于:2 * 5000 = 10000 int loopEnd = 5000; Thread thread_1 = new Thread(() -> { for(int i = 0; i < loopEnd; i++){ demo.addValue(); } }, "thread_1"); Thread thread_2 = new Thread(() -> { for(int i = 0; i < loopEnd; i++){ demo.addValue(); } }, "thread_2"); // 3.啟動執行線程 thread_1.start(); thread_2.start(); // 4.主線程等待子線程執行完成,打印value值 thread_1.join(); thread_2.join(); System.out.println("AtomicInteger型成員變量value最終結果:" + demo.value); } }
執行結果分析:
通過比較普通類型int型變量自增操作,與原子型AtomicInteger型變量自增操作。我們看到應用層代碼幾乎沒有差異,僅僅是通過AtomicInteger替換int實現自增操作,即保證了線程安全。那么AtomicInteger它是如何做到的呢?
要分析清楚AtomicInteger底層原理,還需要回到我們說過的線程安全基本要素:可見性、原子性、有序性。就是說不管通過什么手段,要實現線程安全,一定要滿足這三個基本要素,換句話說,滿足了三個基本要素,也即實現了線程安全。
那么我們就從這三個要素開始分析。首先看最容易理解的有序性,你還記得什么是有序性嗎?它是說線程內有序,線程之間無序。有序性比較好理解,我們就不過多解釋了。
再來看可見性,同樣你還記得什么是可見性嗎?我們知道jmm內存模型,每個線程都有自己的私人空間(工作內存),所有線程共享公共空間(主內存)。那么如果要保證某個變量在線程間的可見性,即當線程A操作該變量后,需要同步將變量值從私人空間同步到公共空間:工作內存--->主內存;同理其它線程在操作變量前,需要從公共空間將變量值同步到私人空間:主內存--->工作內存。java編程語法上給我們提供了一個關鍵字:volatile。用于實現可見性。你可能還需要下面這個圖:
最后再來看原子性,原子性你應該還記得,我們上一篇:高級并發編程系列十二(一文搞懂cas)剛剛分享過。cas本質上是不到黃河心不死,什么意思呢?即是不釋放cpu,循環操作,直到操作成功為止。我們是這么解釋的,你也應該還記得對吧。而且我們還說過對于cas,它的操作原理是三個值:內存值A、期望值B、更新值C。每次操作都會比較內存值A,是否等于期望值B、如果等于則將內存值更新成值C,操作成功;如果內存值A,不等于期望值B,則操作失敗,進行下一次循環操作。你可能還需要下面這個圖:
好了到這里,我們可以一起來看AtomicInteger的源碼了。看看是否滿足我們說的可見性、原子性。進一步分析清楚AtomicInteger類線程安全的實現原理。下面我們通過截圖+文字描述的方式,方便你理解。
先來看AtomicInteger類的聲明,這一塊對于不熟悉的朋友可能比較難看懂,我們先截圖看一下。
通過類聲明部分源碼,我們看到線程安全的可見性,通過volatile關鍵字修飾value成員變量,已經有了保障。那么原子性,又是如何保障的呢?答案是通過Unsafe工具類,進行cas操作來保障的。看圖:
相信通過上面的分析,你已經理解了原子類線程安全的底層實現原理,如果你理解起來稍微還有點難度,我建議你多看兩遍。對于一個程序員來說,我們不應該只會用用框架,底層思想和原理才是內功。
那么關于原子類的底層分析,我們暫時放一放,下面我們一起來看一下juc包中提供的常見原子能力工具類。它們每一個的底層原理,都在上面分析過了,我就不再逐一分析了,只是簡單的列舉出來,如果你感興趣的話,可以找一兩個按照我上面的分析思路,自己分析一下,應該會有意想不到的驚喜!
基本原子類,代表:AtomicInteger、AtomicLong
數組原子類,代表:AtomicIntegerArray、AtomicLongArray
引用原子類,代表:AtomicReference<V>。關于引用原子類,稍微加一句:它可以把一個普通對象,包裝成具有原子能力的對象
提供升級能力原子類,代表:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater
累加器原子類,代表:LongAdder。關于累加器,稍微多加一句:它是jdk1.8開始后新加入的小伙伴,性能比起AtomicLong來說杠杠的。
“高級并發編程系列之什么是原子類”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。