死磕 java原子類之終結篇(面試題)

来源:https://www.cnblogs.com/tong-yuan/archive/2019/05/13/Atomic.html
-Advertisement-
Play Games

概覽 原子操作是指不會被線程調度機制打斷的操作,這種操作一旦開始,就一直運行到結束,中間不會有任何線程上下文切換。 原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分,將整個操作視作一個整體是原子性的核心特征。 在java中提供了很多原子類,筆者 ...


概覽

原子操作是指不會被線程調度機制打斷的操作,這種操作一旦開始,就一直運行到結束,中間不會有任何線程上下文切換。

原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分,將整個操作視作一個整體是原子性的核心特征。

在java中提供了很多原子類,筆者在此主要把這些原子類分成四大類。

atomic

原子更新基本類型或引用類型

如果是基本類型,則替換其值,如果是引用,則替換其引用地址,這些類主要有:

(1)AtomicBoolean

原子更新布爾類型,內部使用int類型的value存儲1和0表示true和false,底層也是對int類型的原子操作。

(2)AtomicInteger

原子更新int類型。

(3)AtomicLong

原子更新long類型。

(4)AtomicReference

原子更新引用類型,通過泛型指定要操作的類。

(5)AtomicMarkableReference

原子更新引用類型,內部使用Pair承載引用對象及是否被更新過的標記,避免了ABA問題。

(6)AtomicStampedReference

原子更新引用類型,內部使用Pair承載引用對象及更新的郵戳,避免了ABA問題。

這幾個類的操作基本類似,底層都是調用Unsafe的compareAndSwapXxx()來實現,基本用法如下:

private static void testAtomicReference() {
    AtomicInteger atomicInteger = new AtomicInteger(1);
    atomicInteger.incrementAndGet();
    atomicInteger.getAndIncrement();
    atomicInteger.compareAndSet(3, 666);
    System.out.println(atomicInteger.get());

    AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
    atomicStampedReference.compareAndSet(1, 2, 1, 3);
    atomicStampedReference.compareAndSet(2, 666, 3, 5);
    System.out.println(atomicStampedReference.getReference());
    System.out.println(atomicStampedReference.getStamp());
}

原子更新數組中的元素

原子更新數組中的元素,可以更新數組中指定索引位置的元素,這些類主要有:

(1)AtomicIntegerArray

原子更新int數組中的元素。

(2)AtomicLongArray

原子更新long數組中的元素。

(3)AtomicReferenceArray

原子更新Object數組中的元素。

這幾個類的操作基本類似,更新元素時都要指定在數組中的索引位置,基本用法如下:

private static void testAtomicReferenceArray() {
    AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
    atomicIntegerArray.getAndIncrement(0);
    atomicIntegerArray.getAndAdd(1, 666);
    atomicIntegerArray.incrementAndGet(2);
    atomicIntegerArray.addAndGet(3, 666);
    atomicIntegerArray.compareAndSet(4, 0, 666);
    
    System.out.println(atomicIntegerArray.get(0));
    System.out.println(atomicIntegerArray.get(1));
    System.out.println(atomicIntegerArray.get(2));
    System.out.println(atomicIntegerArray.get(3));
    System.out.println(atomicIntegerArray.get(4));
    System.out.println(atomicIntegerArray.get(5));
}

原子更新對象中的欄位

原子更新對象中的欄位,可以更新對象中指定欄位名稱的欄位,這些類主要有:

(1)AtomicIntegerFieldUpdater

原子更新對象中的int類型欄位。

(2)AtomicLongFieldUpdater

原子更新對象中的long類型欄位。

(3)AtomicReferenceFieldUpdater

原子更新對象中的引用類型欄位。

這幾個類的操作基本類似,都需要傳入要更新的欄位名稱,基本用法如下:

private static void testAtomicReferenceField() {
    AtomicReferenceFieldUpdater<User, String> updateName = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class,"name");
    AtomicIntegerFieldUpdater<User> updateAge = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

    User user = new User("tong ge", 21);
    updateName.compareAndSet(user, "tong ge", "read source code");
    updateAge.compareAndSet(user, 21, 25);
    updateAge.incrementAndGet(user);
    
    System.out.println(user);
}

private static class User {
    volatile String name;
    volatile int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name: " + name + ", age: " + age;
    }
}

高性能原子類

高性能原子類,是java8中增加的原子類,它們使用分段的思想,把不同的線程hash到不同的段上去更新,最後再把這些段的值相加得到最終的值,這些類主要有:

(1)Striped64

下麵四個類的父類。

(2)LongAccumulator

long類型的聚合器,需要傳入一個long類型的二元操作,可以用來計算各種聚合操作,包括加乘等。

(3)LongAdder

long類型的累加器,LongAccumulator的特例,只能用來計算加法,且從0開始計算。

(4)DoubleAccumulator

double類型的聚合器,需要傳入一個double類型的二元操作,可以用來計算各種聚合操作,包括加乘等。

(5)DoubleAdder

double類型的累加器,DoubleAccumulator的特例,只能用來計算加法,且從0開始計算。

這幾個類的操作基本類似,其中DoubleAccumulator和DoubleAdder底層其實也是用long來實現的,基本用法如下:

private static void testNewAtomic() {
    LongAdder longAdder = new LongAdder();
    longAdder.increment();
    longAdder.add(666);
    System.out.println(longAdder.sum());

    LongAccumulator longAccumulator = new LongAccumulator((left, right)->left + right * 2, 666);
    longAccumulator.accumulate(1);
    longAccumulator.accumulate(3);
    longAccumulator.accumulate(-4);
    System.out.println(longAccumulator.get());
}

問題

關於原子類的問題,筆者整理了大概有以下這些:

(1)Unsafe是什麼?

(3)Unsafe為什麼是不安全的?

(4)Unsafe的實例怎麼獲取?

(5)Unsafe的CAS操作?

(6)Unsafe的阻塞/喚醒操作?

(7)Unsafe實例化一個類?

(8)實例化類的六種方式?

(9)原子操作是什麼?

(10)原子操作與資料庫ACID中A的關係?

(11)AtomicInteger怎麼實現原子操作的?

(12)AtomicInteger主要解決了什麼問題?

(13)AtomicInteger有哪些缺點?

(14)ABA是什麼?

(15)ABA的危害?

(16)ABA的解決方法?

(17)AtomicStampedReference是怎麼解決ABA的?

(18)實際工作中遇到過ABA問題嗎?

(19)CPU的緩存架構是怎樣的?

(20)CPU的緩存行是什麼?

(21)記憶體屏障又是什麼?

(22)偽共用是什麼原因導致的?

(23)怎麼避免偽共用?

(24)消除偽共用在java中的應用?

(25)LongAdder的實現方式?

(26)LongAdder是怎麼消除偽共用的?

(27)LongAdder與AtomicLong的性能對比?

(28)LongAdder中的cells數組是無限擴容的嗎?

關於原子類的問題差不多就這麼多,都能回答上來嗎?點擊下麵的鏈接可以直接到相應的章節查看:

死磕 java魔法類之Unsafe解析

死磕 java原子類之AtomicInteger源碼分析

死磕 java原子類之AtomicStampedReference源碼分析

雜談 什麼是偽共用(false sharing)?

死磕 java原子類之LongAdder源碼分析

彩蛋

原子類系列源碼分析到此就結束了,雖然分析的類比較少,但是牽涉的內容非常多,特別是操作系統底層的知識,比如CPU指令、CPU緩存架構、記憶體屏障等。

下一章,我們將進入“同步系列”,同步最常見的就是各種鎖了,這裡會著重分析java中的各種鎖、各種同步器以及分散式鎖相關的內容。


歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

qrcode


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 內容簡介 本書面向所有對機器學習與數據挖掘的實踐及競賽感興趣的讀者,從零開始,以Python編程語言為基礎,在不涉及大量數學模型與複雜編程知識的前提下,逐步帶領讀者熟悉並且掌握當下最流行的機器學習、數據挖掘與自然語言處理工具,如Scikitlearn、NLTK、Pandas、gensim、XGBo ...
  • Java9的日誌級別: ALL 最低級別,系統會輸出所有的日誌信息,會生成大量的·、冗餘的日誌 TRACE 輸出系統的各種跟蹤信息,會生成大量的·、冗餘的日誌 DEBUG 輸出調試信息,會生成較多的日誌 INFO 輸出系統提示用戶的信息 WARNING 輸出警告信息 ERROR 輸出錯誤信息 OFF ...
  • 筆記 7天學完Java基礎之0/7 1.常用命令提示符(cmd) 啟動: Win+R,輸入cmd​ :twisted_rightwards_arrows: cmd 切換盤符 盤符名稱+:(冒號為英文輸入法下的冒號) 進入指定文件夾 cd +文件夾名稱 註意只能夠進入文件夾,不能夠進入文件哦 查看當前 ...
  • 國際化是指應用程式運行時,可根據客戶端OS的國家/地區、語言的不同而顯示不同的界面,比如客戶端OS的語言環境為大陸的簡體中文,程式就顯示為簡體中文,客戶端OS的語言環境為美國——英語,程式就顯示美式英語。 OS的語言環境可在控制面板中手動設置。 國際化的英文單詞是Internationalizati ...
  • Python進階之網路編程,內容包括 網路通信,socket,udp,tcp。其中,網路通信 包括 使用網路的目的,ip地址,埠;socket 包括 socket的概念,創建socket;udp 包括 udp發送數據,udp接收數據,udp接發數據總結,埠綁定的問題,單工半雙工全雙工;tcp 包... ...
  • "概要" "開發" "web 框架" "資料庫" "認證" "日誌" "配置" "靜態文件服務" "上傳/下載" "發佈" "docker 打包" "部署中遇到的問題" "時區問題" 概要 輕量的基於 golang 的 web 開發實踐. golang 上手簡單, 第三方庫豐富, 對於業務沒那麼複雜 ...
  • 1、正則表達式語法: ...
  • 深入理解Java中的spi機制 全名為 是JDK內置的一種服務提供發現機制,是Java提供的一套用來被第三方實現或者擴展的API,它可以用來啟用框架擴展和替換組件。 = 基於介面的編程+策略模式+配置文件 的動態載入機制 Java SPI的具體約定如下: 當服務的提供者,提供了服務介面的一種實現之後 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...