Java并發(fā)中的原子類詳細(xì)代碼實(shí)例
1. 為什么需要原子類
多線程環(huán)境下,有個(gè)常見(jiàn)問(wèn)題:
public class Counter {
private int count = 0;
public void increment() {
count++;
}
}
count++ 看似一行代碼,實(shí)際分三步:
- 讀取 count 的值
- 加 1
- 寫回 count
多線程下可能發(fā)生:
線程A:讀取 count=0
線程B:讀取 count=0
線程A:count=1,寫回
線程B:count=1,寫回 // 線程A的結(jié)果被覆蓋了
最終 count=1,但實(shí)際應(yīng)該等于 2。
2. 解決辦法
synchronized 加鎖
public synchronized void increment() {
count++;
}
缺點(diǎn):性能差,每次只能一個(gè)線程進(jìn)入。
原子類
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
一行搞定,性能比 synchronized 高很多。
3. 常用原子類
AtomicInteger
AtomicInteger count = new AtomicInteger(0); // 加1 count.incrementAndGet(); // ++i count.getAndIncrement(); // i++ // 減1 count.decrementAndGet(); // --i count.getAndDecrement(); // i-- // 加任意值 count.addAndGet(5); // 獲取值 int value = count.get(); // 設(shè)置值 count.set(100); // CAS 操作 count.compareAndSet(100, 200); // 如果當(dāng)前值是100,則設(shè)置為200
AtomicLong
和 AtomicInteger 用法一樣,只是類型不同:
AtomicLong count = new AtomicLong(0); count.incrementAndGet();
AtomicBoolean
AtomicBoolean flag = new AtomicBoolean(false); // 設(shè)為 true flag.set(true); // CAS 操作 flag.compareAndSet(false, true);
AtomicReference
用來(lái)原子更新對(duì)象:
AtomicReference<User> userRef = new AtomicReference<>();
User user1 = new User("張三", 20);
User user2 = new User("李四", 25);
userRef.set(user1);
userRef.compareAndSet(user1, user2); // 如果當(dāng)前是 user1,則改為 user2
4. 數(shù)組原子類
// 數(shù)組中某個(gè)元素原子操作
AtomicIntegerArray arr = new AtomicIntegerArray(new int[]{1, 2, 3});
arr.getAndAdd(0, 10); // 第一個(gè)元素加10
int value = arr.get(0); // 獲取第一個(gè)元素
5. 累加器
jdk8 新增的,比 AtomicInteger 性能更高,專門用于累加場(chǎng)景:
LongAdder count = new LongAdder(); count.add(1); count.increment(); long value = count.sum();
內(nèi)部原理是把一個(gè)值拆成多個(gè) Cell,減少競(jìng)爭(zhēng)。適合高并發(fā)場(chǎng)景下的累加。
// AtomicInteger vs LongAdder // 低并發(fā):兩者差不多 // 高并發(fā):LongAdder 性能更好
6. 實(shí)戰(zhàn)示例
計(jì)數(shù)器
public class PageViewCounter {
private AtomicInteger count = new AtomicInteger(0);
public void addView() {
count.incrementAndGet();
}
public int getViews() {
return count.get();
}
}
防止重復(fù)提交
public class SubmitService {
private AtomicBoolean submitting = new AtomicBoolean(false);
public void submit() {
// 如果已經(jīng)在提交中,直接返回
if (!submitting.compareAndSet(false, true)) {
return;
}
try {
// 業(yè)務(wù)邏輯
doSubmit();
} finally {
submitting.set(false);
}
}
}
配置更新
public class ConfigHolder {
private AtomicReference<Config> configRef = new AtomicReference<>();
public void updateConfig(Config newConfig) {
configRef.updateAndGet(old -> newConfig);
}
public Config getConfig() {
return configRef.get();
}
}
7. 總結(jié)
| 類 | 用途 |
|---|---|
| AtomicInteger | 整數(shù)原子操作 |
| AtomicLong | 長(zhǎng)整數(shù)原子操作 |
| AtomicBoolean | 布爾原子操作 |
| AtomicReference | 對(duì)象引用原子操作 |
| AtomicIntegerArray | 整數(shù)數(shù)組原子操作 |
| LongAdder | 高并發(fā)累加,比 AtomicLong 性能更好 |
什么時(shí)候用:
- 需要保證多線程安全地修改一個(gè)值時(shí)
- 簡(jiǎn)單計(jì)數(shù)、狀態(tài)標(biāo)記等場(chǎng)景
- 比 synchronized 性能更好
到此這篇關(guān)于Java并發(fā)中原子類的文章就介紹到這了,更多相關(guān)Java并發(fā)原子類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring mvc整合mybatis(crud+分頁(yè)插件)操作mysql
這篇文章主要介紹了Spring mvc整合mybatis(crud+分頁(yè)插件)操作mysql的步驟詳解,需要的朋友可以參考下2017-04-04
Prometheus 入門教程之SpringBoot 實(shí)現(xiàn)自定義指標(biāo)監(jiān)控
這篇文章主要介紹了Prometheus 入門教程之SpringBoot 實(shí)現(xiàn)自定義指標(biāo)監(jiān)控,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
你不知道的 IDEA Debug調(diào)試小技巧(小結(jié))
這篇文章主要介紹了你不知道的 IDEA Debug調(diào)試小技巧(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
java開(kāi)發(fā)實(shí)現(xiàn)訂閱到貨通知幫我們買到想買的東西
這篇文章主要為大家介紹了java開(kāi)發(fā)實(shí)現(xiàn)訂閱到貨通知幫我們買到想買的東西示例demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

