JAVA並發之AtomicInteger原理分析

2022-01-01 小西學JAVA

本篇文章我們以AtomicInteger為例子,主要講解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe對象。

我們先從一個例子開始吧。假設現在我們要實現多線程應用中的int值自增(單個應用範圍),應該怎麼做呢?

我們可能首先想到的是利用synchronized關鍵字,大概的代碼如下:

private static volatile int value;

public static void main(String[] args) {
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
increaseBySync();
}
}
};

Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t1.start();
t2.start();

while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(value);
}

private static synchronized int increaseBySync() {
return value++;
}

上面的例子有兩個線程同時對value變量做自增操作,通過對increaseBySync方法加synchronized鎖實現了線程安全的int值自增。

synchronized性能問題

當多個線程訪問某個syncronized方法或者代碼塊的時候,線程間的切換和其他線程等待的時間間隔(取決於OS實現,存在不確定性),由此帶來的性能損耗是比較大的。從JDK5開始,我們可以藉助於
java.util.concurrent.atomic包所提供的一些工具類,來實現上述的功能,AtomicInteger就是其中的一個類,它使用了CPU級別的CAS功能,利用它我們可以寫出更高效的代碼。

private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
atomicInteger.incrementAndGet();
}
}
};

Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t1.start();
t2.start();

while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(atomicInteger.get());
}

上面的代碼同樣能達到多線程自增的效果,但是更高效。

AtomicInteger源碼分析

下面我們來看下AtomicInteger內部是如何實現的呢。

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;

首先它有一個用volatile修飾的int變量value,volatile這裡主要是保證對value值的更新對每個線程的可見性,關於volatile的介紹可以參考文章末的連結。

這裡比較關鍵的是Unsafe類型的對象。通過它的名字JDK開發者想告訴我們它是不安全的,但是它又提供了一些JVM無法提供的功能,例如CAS就是其中一個。

CAS

下面是AtomicInteger類中的一個方法,它通過Unsafe對象調用了CAS功能。CAS是目前幾乎所有CPU都提供的一個功能,而且是原子操作,藉助它可以實現很多高效的功能。

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

其他atomic工具類


java.util.concurrent.atomic包下面Atomic開頭的類都是基於CAS實現的,大家有興趣可以看看源碼,如果理解了上面所說的,其他的都很類似。

AtomicBoolean

AtomicIntegerArray

AtomicLong

AtomicReference

。。。

其他文章

Java並發之volatile關鍵字

Demo代碼位置


src/main/java/net/weichitech/juc/AtomicIntegerTest.java · 小西學編程/java-learning - Gitee.com

相關焦點

  • 詳解java並發原子類AtomicInteger(基於jdk1.8源碼分析)
    java並發包裡面的類一直是學習和面試的重點,這篇文章主要是對java並發包的其中一個類AtomicInteger的講解。從為什麼要出現AtomicInteger再到其底層原理來一個分析。一、從a++說起為什麼使用AtomicInteger我們知道java並發機制中主要有三個特性需要我們去考慮,原子性、可見性和有序性。
  • 不可不知的JUC:原子操作類AtomicLong的原理分析
    關注我,了解更多你不知道的【Java後端】打工技巧、職場經驗、生活感悟等一、前言java.util.concurrent 包裡提供了一系列的原子性操作類,這些類都是使用CAS機制實現的,相當於使用鎖實現原子性操作在性能上有了很大的提高。
  • Java並發編程之支持並發的list集合你知道嗎
    Java並發編程之-list集合的並發.我們都知道Java集合類中的arrayList是線程不安全的。那麼怎麼證明是線程不安全的呢?怎麼解決在並發環境下使用安全的list集合類呢?本篇是《凱哥(凱哥Java:kagejava)並發編程學習》系列之《並發集合系列》教程的第一篇:本文主要內容:怎麼證明arrayList不是線程安全的?怎麼解決這個問題?以及遇到問題解決的四個步驟及從源碼來分析作者思路。一:怎麼證明arrayList在並發情況下是線程不安全的呢?
  • java並發編程-原子類
    而 java.util.concurrent.atomic 下的類,就是具有原子性的類,可以原子性地執行添加、遞增、遞減等操作。比如之前多線程下的線程不安全的 i++ 問題,到了原子類這裡,就可以用功能相同且線程安全的 getAndIncrement 方法來優雅地解決。
  • Java從零開始學- 第32天:高並發中計數器的實現方式有哪些?
    點擊上方關注 「Java研究所」設為「星標」,和你一起掌握更多資料庫知識這是java高並發系列第32篇文章。java環境:jdk1.8。;import java.util.concurrent.ExecutionException;import java.util.concurrent.atomic.LongAccumulator;/** * 跟著阿里p7學並發,微信公眾號:javacode2018 */public class Demo1 {    static int
  • 原創】Java並發編程系列01|開篇獲獎感言
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫為什麼要學並發編程我曾聽一個從事15年開發工作的技術人員說過1.並發理論:並發編程要解決的三大問題;介紹可見性與有序性問題的根源重排序;學習Java內存模型(JMM),理解JMM如何解決這些問題以實現並發編程的。2.並發關鍵字:深入volatile、synchronized、final關鍵字的作用,都解決了什麼問題,以及其實現原理。
  • Java 中 Integer 源碼學習之緩存池了解
    收錄於話題 #java答案是 -128~127;編譯器會在緩衝池範圍內的基本類型自動裝箱過程調用 valueOf() 方法,因此多個 Integer 實例使用自動裝箱來創建並且值相同,那麼就會引用相同的對象。
  • JAVA並發編程:線程並發工具類Callable、Future 和FutureTask的使用
    2、代碼示例package cn.lspj.ch2.future;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException
  • Java並發編程之volatile關鍵字解析
    由於volatile關鍵字是與Java的內存模型有關的,因此在講述volatile關鍵之前,我們先來了解一下與內存模型相關的概念和知識,然後分析了volatile關鍵字的實現原理,最後給出了幾個使用volatile關鍵字的場景。
  • 線程安全之std::atomic探索
    從不同線程訪問某個原子對象是良性(well-defined) 行為,而通常對於非原子類型而言,並發訪問某個對象(如果不做任何同步操作)會導致未定義(undefined) 行為發生。因此,從實現的原理上來,可以理解為原子類型在自己內部添加了互斥鎖。我們先創建一個CMake工程,並創建兩個線程來操作同一個全局變量,然後來編譯運行,查看它的結果。
  • 徹底理解Java並發編程原理!
    Java並發編程為四大部分:計算機並發基礎知識、JDK內置並發框架、JDK並發包剖析以及其它並發知識。具體包括線程的狀態、Java線程調度策略、線程優先級、並發模型、悲觀鎖樂觀鎖、JDK各種同步器、JDK內置AQS同步器、線程與IO、Java線程與JVM線程、阻塞與喚醒機制、JDK並發包各種工具剖析、自旋、JDK內置並發鎖、CAS、synchronized、線程池、線程之間的協作等並發方面知識及原理進行深入淺出的講解。
  • 【死磕Java並發】深入分析synchronized實現原理
    同步代碼塊:monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結束位置,JVM需要保證每一個monitorenter都有一個monitorexit與之相對應。任何對象都有一個monitor與之相關聯,當且一個monitor被持有之後,他將處於鎖定狀態。
  • Java並發編程學習前期知識上篇
    Java並發編程-前期準備知識-上我們先來看看幾個大廠真實的面試題:從上面幾個真實的面試問題來看,我們可以看到大廠的面試都會問到並發相關的問題。所以Java並發,這個無論是面試還是在工作中,並發都是會遇到的。
  • java面試技巧:Integer和int的那些事
    Integer的動裝箱和動拆箱的原理是什麼?以及所發在哪個階段?帶來的好處和壞處是什麼? 什麼情況下會發動裝箱/動拆箱?那你能說下Integer的值緩存是什麼嗎?你覺得 Integer的源碼有哪些設計要點?
  • Java 並發編程:Synchronized 及其實現原理
    的原理,再回頭上面的問題就一目了然了。通過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴於monitor對象,這就是為什麼只有在同步的塊或者方法中才能調用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。
  • Java多線程並發工具類-信號量Semaphore對象講解
    Java多線程並發工具類-Semaphore對象講解通過前面的學習,我們已經知道了Java多線程並發場景中使用比較多的兩個工具類:做加法的CycliBarrier對象以及做減法的CountDownLatch對象並對這兩個對象進行了比較。我們發現這兩個對象要麼是做加法,要麼是做減法的。那麼有沒有既做加法也做減法的呢?
  • 「原創」Java並發編程系列28|Copy-On-Write容器
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫正文前面兩篇講了並發編程中線程安全HashMap:ConcurrentHashMap
  • Java線程並發之AQS原理機制全面詳解
    前言談到並發,不得不談ReentrantLock;而談到ReentrantLock,不得不談AbstractQueuedSynchronized(AQS);今天我們就來探討下原理;Java多線程鎖機制解剖詳解-解決你心中的困惑,鎖住你的心一、什麼是
  • 並發組件CountDownLatch的底層原理
    比如,如果你以前對並發組件很陌生的話,掌握了之後就會讓你對這些知識的認知提升,使用起來不再心中沒有底。又或者如果你面試經常被這些基礎知識問倒,相信學過之後面試的時候起碼不會汗如雨下,亂說一通。又比如,當你比較熟悉這些知識後,也在開源框架和其他技術中看到這些就會很親切…… 而在接下來的幾節中,會重點介紹一些並發組件和集合。
  • Tomcat 高並發之道原理拆解與性能調優
    高並發拆解核心準備這回,再次拆解,專注 Tomcat 高並發設計之道與性能調優,讓大家對整個架構有更高層次的了解與感悟。如下圖所示,整個 Tomcat 的架構設計重要組件清晰可見,希望大家將這個全局架構圖深深印在腦海裡,掌握全局思路才能更好地分析細節之美。Tomcat 架構啟動流程:startup.sh 腳本到底發生了什麼