Java中的原子類AtomicInteger使用詳解
原子操作
原子操作是指不會(huì)被線程調(diào)度機(jī)制打斷的操作,這種操作一旦開始,就一直運(yùn)行到結(jié)束,中間不會(huì)有任何線程上下文切換。
原子操作可以是一個(gè)步驟,也可以是多個(gè)操作步驟,但是其順序不可以被打亂,也不可以被切割而只執(zhí)行其中的一部分,將整個(gè)操作視作一個(gè)整體是原子性的核心特征。
原子類
在Java中提供了很多原子類,主要把這些原子類分成以下幾大類

原子更新基本類型或引用類型
- AtomicBoolean
- 原子更新布爾類型,內(nèi)部使用int類型的value存儲(chǔ)1和0表示true和false,底層也是對(duì)int類型的原子操作。
- AtomicInteger
- 原子更新int類型。
- AtomicLong
- 原子更新long類型。
- AtomicReference
- 原子更新引用類型,通過泛型指定要操作的類。
- AtomicMarkableReference
- 原子更新引用類型,內(nèi)部使用Pair承載引用對(duì)象及是否被更新過的標(biāo)記,避免了ABA問題。
- AtomicStampedReference
- 原子更新引用類型,內(nèi)部使用Pair承載引用對(duì)象及更新的郵戳,避免了ABA問題。
原子更新數(shù)組中的元素
原子更新數(shù)組中的元素,可以更新數(shù)組中指定索引位置的元素。
- AtomicIntegerArray
原子更新int數(shù)組中的元素。 - AtomicLongArray
原子更新long數(shù)組中的元素。 - AtomicReferenceArray
原子更新object數(shù)組中的元素。
原子更新對(duì)象中的字段
原子更新對(duì)象中的字段,可以更新對(duì)象中指定字段名稱的字段。
- AtomicIntegerFieldUpdater
原子更新對(duì)象中的int類型字段。 - AtomicLongFieldUpdater
原子更新對(duì)象中的long類型字段。 - AtomicReferenceFieldUpdater
原子更新對(duì)象中的引用類型字段。
高性能原子類
高性能原子類,是Java8中增加的原子類,它們使用分段的思想,把不同線程hash到不同的段上去更新,最后再把這些段的值相加得到最終的值。
Striped64
下面四個(gè)類的父類。
- LongAccumulator
- long類型的聚合器,需要傳入一個(gè)long類型的二元操作,可以用來計(jì)算各種聚合操作,包括加乘等。
- LongAdder
- long類型聚合器,LongAccumulator的特例,只能用來計(jì)算加法,且從零開始計(jì)算。
- DoubleAccumulator
- double類型的聚合器,需要傳入一個(gè)double類型的二元操作,可以用來計(jì)算各種聚合操作,包括加乘等。
- DoubleAdder
- double類型聚合器,DoubleAccumulator的特例,只能用來計(jì)算加法,且從零開始計(jì)算。
AtomicInteger使用
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
public static void main(String[] args) throws InterruptedException {
test1();
test2();
}
private static void test1() throws InterruptedException {
Counter counter = new Counter();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
counter.addCount();
}
}).start();
}
Thread.sleep(1000);
System.out.println("test1 count = " + counter.getCount());
}
private static void test2() throws InterruptedException {
AtomicInteger count = new AtomicInteger();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
count.incrementAndGet();
}
}).start();
}
Thread.sleep(1000);
System.out.println("test2 count = " + count.get());
}
}
public class Counter {
private volatile static int count = 0;
public void addCount(){
count++;
}
public int getCount(){
return count;
}
}運(yùn)行以上代碼會(huì)發(fā)現(xiàn),test1的結(jié)果達(dá)不到預(yù)期的10000,而且每次的結(jié)果不可再現(xiàn)。而test2的結(jié)果每次都是10000,是確定的可再現(xiàn)的。
是因?yàn)閠est1中調(diào)用的Counter類的addCount方法,這個(gè)方法不是原子性的。
count++可以分解為以下幾個(gè)原子性的步驟:
1.讀取count的值
2.計(jì)算新值count+1
3.新值寫入count變量
如果步驟1、2、3中有多個(gè)線程并發(fā)執(zhí)行,那么就會(huì)出現(xiàn)兩個(gè)或多個(gè)線程并發(fā)的執(zhí)行+1操作,而我們希望的是每個(gè)線程依次執(zhí)行+1的操作。
AtomicInteger原理
AtomicInteger聲明
public class AtomicInteger extends Number implements java.io.Serializable
Unsafe類的使用
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); }
}AtomicInteger屬性
private volatile int value;
AtomicInteger構(gòu)造器
public AtomicInteger(int initialValue) {
value = initialValue;
}
public AtomicInteger() {
}AtomicInteger自增
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
調(diào)用Unsafe類的方法
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
compareAndSwapInt即CAS方式修改int值:
- 調(diào)用unsafe.getAndAddInt方法。
- unsafe.getAndAddInt方法通過自旋的方式,每次嘗試通過CAS方式對(duì)原值進(jìn)行累加。如果累加失敗,將進(jìn)入下一次循環(huán)。如果累加成功,則自選結(jié)束。
到此這篇關(guān)于Java中的原子類AtomicInteger使用詳解的文章就介紹到這了,更多相關(guān)Java原子類AtomicInteger內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud修改Feign日志記錄級(jí)別過程淺析
OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成2023-02-02
springboot跨域過濾器fetch react Response to p
這篇文章主要介紹了springboot跨域過濾器fetch react Response to preflight request doesn‘t pass access control check問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Java對(duì)象不使用時(shí)賦值null的意義詳解
這篇文章主要介紹了java對(duì)象不再使用時(shí)賦值null的意義,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
List對(duì)象去重和按照某個(gè)字段排序的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狶ist對(duì)象去重和按照某個(gè)字段排序的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
MyBatis-Plus使用ActiveRecord(AR)實(shí)現(xiàn)CRUD
本文將結(jié)合實(shí)例代碼,介紹MyBatis-Plus使用ActiveRecord(AR)實(shí)現(xiàn)CRUD,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07

