日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发编程—Atomic原子类

發布時間:2024/4/15 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程—Atomic原子类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

Atomic

1. AtomicInteger

a. 多線程并發訪問問題

b. 用 AtomicInteger 類解決

2. AtomicIntegerArray

a. 多線程并發訪問問題

b. 用 AtomicIntegerArray 類解決

相關問題


Atomic

在 java.util.concurrent.atomic 包下定義了一些對“變量”操作的“原子類”,它們可以保證對“變量”操作的:原子性、有序性、可見性:

  • java.util.concurrent.atomic.AtomicInteger:對 int 變量操作的“原子類”;
  • java.util.concurrent.atomic.AtomicLong:對 long 變量操作的“原子類”;
  • java.util.concurrent.atomic.AtomicBoolean:對 boolean 變量操作的“原子類”;

1. AtomicInteger

a. 多線程并發訪問問題

class MyThread extends Thread {public static volatile int a = 0;@Overridepublic void run() {for (int i = 0; i < 10000; i++) {//線程1:取出a的值a=0(被暫停)a++;//寫回}System.out.println("修改完畢!");} }public class Test {public static void main(String[] args) throws InterruptedException {//1.啟動兩個線程MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();Thread.sleep(1000);System.out.println("獲取a最終值:" + MyThread.a);//最終結果仍然不正確。} } /* 輸出 修改完畢! 修改完畢! 獲取a最終值:14791*/
  • 加入 volatile 關鍵字并不能解決變量自增操作的原子性問題;

b. 用 AtomicInteger 類解決

  • 用 AtomicInteger 類解決了多線程的原子性問題,無論程序運行多少次,其結果總是正確的;
import java.util.concurrent.atomic.AtomicInteger;class MyThread extends Thread {//public static volatile int a = 0;//不直接使用基本類型變量//改用"原子類"public static AtomicInteger a = new AtomicInteger(0);@Overridepublic void run() {for (int i = 0; i < 10000; i++) {// a++;a.getAndIncrement();//先獲取,再自增1:a++}System.out.println("修改完畢!");} }public class Test {public static void main(String[] args) throws InterruptedException {//1.啟動兩個線程MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();Thread.sleep(1000);System.out.println("獲取a最終值:" + MyThread.a.get());} } /* 輸出 修改完畢! 修改完畢! 獲取a最終值:20000*/

c. 工作原理:CAS 機制

在 Unsafe 類中,調用了一個:compareAndSwapInt() 方法,此方法的幾個參數:

  • var1:傳入的 AtomicInteger 對象
  • var2:AtommicInteger 內部變量的偏移地址
  • var5:之前取出的 AtomicInteger 中的值
  • var5 + var4:預期結果

此方法使用了一種"比較并交換(Compare And Swap)"的機制,它會用 var1 和 var2 先獲取內存中 AtomicInteger 中的值,然后和傳入的,之前獲取的值 var5 做一下比較,也就是比較當前內存的值和預期的值是否一致,如果一致就修改為 var5 + var4,否則就繼續循環,再次獲取 AtomicInteger 中的值,再進行比較并交換,直至成功交換為止;compareAndSwapInt() 方法是"線程安全"的;我們假設兩個線程交替運行的情況,看看它是怎樣工作的:

  • 初始 AtomicInteger 的值為0
  • 線程A執行:var5 = this.getIntVolatile(var1,var2);獲取的結果為:0
  • 線程A被暫停
  • 線程B執行:var5 = this.getIntVolatile(var1,var2);獲取的結果為:0
  • 線程B執行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
  • 線程B成功將 AtomicInteger 中的值改為1
  • 線程A恢復運行,執行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
  • 此時線程A使用 var1 和 var2 從 AtomicInteger 中獲取的值為:1,而傳入的 var5 為0,比較失敗,返回 false,繼續循環
  • 線程A執行:var5 = this.getIntVolatile(var1,var2);獲取的結果為:1
  • 線程A執行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
  • 此時線程A使用 var1 和 var2 從 AtomicInteger 中獲取的值為:1,而傳入的var5為1,比較成功,將其修改為 var5 + var4,也就是2,將 AtomicInteger 中的值改為2,結束;

CAS 機制也被稱為樂觀鎖機制、自旋鎖機制,因為大部分比較的結果為 true,就直接修改了。只有少部分多線程并發的情況會導致 CAS 失敗,而再次循環;

  • 關于樂觀鎖與悲觀鎖的實際應用;
  • 關于CAS理論

2. AtomicIntegerArray

常用的數組操作的原子類,它們可以解決數組的多線程并發訪問的安全性問題:

  • java.util.concurrent.atomic.AtomicIntegetArray:對 int 數組操作的原子類;
  • java.util.concurrent.atomic.AtomicLongArray:對 long 數組操作的原子類;
  • java.utio.concurrent.atomic.AtomicReferenceArray:對引用類型數組操作的原子類;

a. 多線程并發訪問問題

class MyThread extends Thread {private static int[] intArray = new int[1000];//不直接使用數組@Overridepublic void run() {for (int i = 0; i < getIntArrayLength(); i++) {intArray[i]++;}}public static int getIntArray(int i) {return intArray[i];}public static int getIntArrayLength() {return intArray.length;} }public class Test {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {new MyThread().start();//創建1000個線程,每個線程為數組的每個元素+1}Thread.sleep(1000 * 5);//讓所有線程執行完畢System.out.println("主線程休息5秒醒來");for (int i = 0; i < MyThread.getIntArrayLength(); i++) {System.out.println(MyThread.getIntArray(i));}} } /* 部分輸出 ... 999 999 1000 1000 ...*/
  • 可以發現,有些元素并不是1000;

b. 用 AtomicIntegerArray 類解決

  • AtomicIntegerArray 類可以保證數組的多線程安全;
import java.util.concurrent.atomic.AtomicIntegerArray;class MyThread extends Thread {private static int[] intArray = new int[1000];//定義一個數組//改用原子類,使用數組構造public static AtomicIntegerArray arr = new AtomicIntegerArray(intArray);@Overridepublic void run() {for (int i = 0; i < arr.length(); i++) {arr.addAndGet(i, 1);//將i位置上的元素+1}} }public class Test {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {new MyThread().start();}Thread.sleep(1000 * 5);//讓所有線程執行完畢System.out.println("主線程休息5秒醒來");for (int i = 0; i < MyThread.arr.length(); i++) {System.out.println(MyThread.arr.get(i));}} } /* 輸出 1000 1000 1000 1000 ...*/
  • 可見,每次運行的結果都是正確的;

相關問題

1:為什么會出現Atomic類

  在多線程或者并發環境中,我們常常會遇到這種情況 int i=0; i++ 稍有經驗的同學都知道這種寫法是線程不安全的。為了達到線程安全的目的,我們通常會用synchronized來修飾對應的代碼塊。現在我們有了新的方法,就是使用J.U.C包下的atomic類。

2:Atomic類的原理是什么呢

一句話來說,atomic類是通過自旋CAS操作volatile變量實現的。CAS是compare and swap的縮寫,即比較后(比較內存中的舊值與預期值)交換(將舊值替換成預期值)。它是sun.misc包下Unsafe類提供的功能,需要底層硬件指令集的支撐。使用volatile變量是為了多個線程間變量的值能及時同步。

3:為什么使用Atomic類

按理來說,使用synchroized已經能滿足功能需求了。為什么還會有這個類呢?那肯定是性能的問題了。在JDK1.6之前,synchroized是重量級鎖,即操作被鎖的變量前就對對象加鎖,不管此對象會不會產生資源競爭。這屬于悲觀鎖的一種實現方式。而CAS會比較內存中對象和當前對象的值是否相同,相同的話才會更新內存中的值,不同的話便會返回失敗。這是樂觀鎖的一中實現方式。這種方式就避免了直接使用內核狀態的重量級鎖。但是在JDK1.6以后,synchronized進行了優化,引入了偏向鎖,輕量級鎖,其中也采用了CAS這種思想,效率有了很大的提升。

4:Atomic類的缺點

1)ABA問題:對于一個舊的變量值A,線程2將A的值改成B又改成可A,此時線程1通過CAS看到A并沒有變化,但實際A已經發生了變化,這就是ABA問題。解決這個問題的方法很簡單,記錄一下變量的版本就可以了,在變量的值發生變化時對應的版本也做出相應的變化,然后CAS操作時比較一下版本就知道變量有沒有發生變化。atomic包下AtomicStampedReference類實現了這種思路。Mysql中Innodb的多版本并發鎖也是這個原理

2)自旋問題:atomic類會多次嘗試CAS操作直至成功或失敗,這個過程叫做自旋。通過自旋的過程我們可以看出自旋操作不會將線程掛起,從而避免了內核線程切換,但是自旋的過程也可以看做CPU死循環,會一直占用CPU資源。這種情形在單CPU的機器上是不能容忍的,因此自旋一般都會有個次數限制,即超過這個次數后線程就會放棄時間片,等待下次機會。因此自旋操作在資源競爭不激烈的情況下確實能提高效率,但是在資源競爭特別激烈的場景中,CAS操作會的失敗率就會大大提高,這時使用中重量級鎖的效率可能會更高。當前,也可以使用LongAdder類來替換,它則采用了分段鎖的思想來解決并發競爭的問題。

總結

以上是生活随笔為你收集整理的Java并发编程—Atomic原子类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。