成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

盤點Java中基于CAS實現的原子類

開發 前端
本文總結了JDK中提供的各種原子類,包括基礎原子類、原子引用類、原子數組類、原子字段更新器和原子累加器等。有時候,使用這些原子類的性能是比加鎖要高的,特別是在讀多寫少的場景下。

?前言

JDK中提供了一系列的基于CAS實現的原子類,CAS 的全稱是Compare-And-Swap?,底層是lock cmpxchg指令,可以在單核和多核 CPU 下都能夠保證比較交換的原子性。所以說,這些原子類都是線程安全的,而且是無鎖并發,線程不會頻繁上下文切換,所以在某些場景下性能是優于加鎖。

本文就盤點一下JDK中的原子類,方便我們后續拿來使用。

圖片

基礎原子類

  • AtomicInteger:Integer整數類型的原子操作類
  • AtomicBoolean:Boolean類型的原子操作類
  • AtomicLong:Long類型的原子操作類

這邊以AtomicInteger講解下它的API和用法。

構造方法:

  • public AtomicInteger()?:初始化一個默認值為 0 的原子型 Integer
  • public AtomicInteger(int initialValue)?:初始化一個指定值的原子型 Integer

常用API:

  • public final int get(): 獲取 AtomicInteger 的值
  • public final int getAndIncrement(): 以原子方式將當前值加 1,返回的是自增前的值
  • public final int incrementAndGet():以原子方式將當前值加 1,返回的是自增后的值
  • public final int getAndSet(int value):以原子方式設置為 newValue 的值,返回舊值
  • public final int addAndGet(int data):以原子方式將輸入的數值與實例中的值相加并返回

使用:

圖片

  • 結果1000,大致說明并發情況下保證了線程安全

原理分析:

整體實現思路: 自旋(循環) + CAS算法

  • 當舊的預期值 A == 內存值 V 此時可以修改,將 V 改為 B
  • 當舊的預期值 A != 內存值 V 此時不能修改,并重新獲取現在的最新值,重新獲取的動作就是自旋
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

valueOffset:偏移量表示該變量值相對于當前對象地址的偏移,Unsafe 就是根據內存偏移地址獲取數據

圖片

從主內存中拷貝到工作內存中的值(每次都要從主內存拿到最新的值到本地內存),然后執行compareAndSwapInt()再和主內存的值進行比較,假設方法返回 false,那么就一直執行 while 方法,直到期望的值和真實值一樣,修改數據。

圖片

原子類AtomicInteger的value?屬性是volatile類型,保證了多線程之間的內存可見性,避免線程從工作緩存中獲取失效的變量。

原子引用

原子引用主要是對對象的原子操作,原子引用類分為AtomicReference、AtomicStampedReference、AtomicMarkableReference。它們之間有什么區別呢?

AtomicReference類

普通的原子類對象

public class AtomicReferenceDemo {
public static void main(String[] args) {
User user1 = new User("旭陽");

// 創建原子引用包裝類
AtomicReference<User> atomicReference = new AtomicReference<>(user1);

while (true) {
User user2 = new User("alvin");
// 比較并交換
if (atomicReference.compareAndSet(user1, user2)) {
break;
}
}
System.out.println(atomicReference.get());
}
}

@Data
@AllArgsConstructor
@ToString
class User {
private String name;
}

調用compareAndSet()方法進行比較替換對象

ABA問題

但是如果使用AtomicReference類,會有一個ABA問題。什么意思呢?就是一個線程將共享變量從A改成B, 后面又改回A, 這是,另外一個線程就無法感知這個變化過程,就傻傻的比較,就以為沒有變化,還是一開始的A,就替換了。 實際的確存在這樣只要共享變量發生過變化,就要CAS失敗,有什么辦法呢?

AtomicStampedReference類

帶版本號的原子類對象

@Slf4j(topic = "a.AtomicStampedReferenceTest")
public class AtomicStampedReferenceTest {
// 構造AtomicStampedReference
static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);

public static void main(String[] args) throws InterruptedException {
log.debug("main start...");
// 獲取值 A
String prev = ref.getReference();
// 獲取版本號
int stamp = ref.getStamp();
log.debug("版本 {}", stamp);
// 如果中間有其它線程干擾,發生了 ABA 現象
other();
Thread.sleep(1000);
// 嘗試改為 C
log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1));
}
private static void other() throws InterruptedException {
new Thread(() -> {
log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B",
ref.getStamp(), ref.getStamp() + 1));
log.debug("更新版本為 {}", ref.getStamp());
}, "t1").start();

Thread.sleep(500);

new Thread(() -> {
log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A",
ref.getStamp(), ref.getStamp() + 1));
log.debug("更新版本為 {}", ref.getStamp());
}, "t2").start();
}
}

圖片

  • 雖然對象的值變回了A,但是由于版本變了,所以主線程CAS失敗

AtomicMarkableReference 類

其實有時候并不關心共享變量修改了幾次,而是只要標記下是否發生過更改,是否加個標記即可,所以就有了AtomicMarkableReference類。

@Slf4j(topic = "c.AtomicMarkableReferenceTest")
public class AtomicMarkableReferenceTest {
// 構造 AtomicMarkableReference, 初始標記為false
static AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", false);

public static void main(String[] args) throws InterruptedException {
log.debug("main start...");
other();
Thread.sleep(1000);
// 看看是否發生了變化
log.debug("change {}", ref.isMarked());
}
private static void other() throws InterruptedException {
new Thread(() -> {
log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B",
false, true));
}, "t1").start();

Thread.sleep(500);

new Thread(() -> {
log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A",
true, true));
}, "t2").start();
}
}

圖片

通過調用isMarked()方法查看是否發生變化。

原子數組

  • AtomicIntegerArray: Integer類型的原子數組
  • AtomicLongArray:Long類型的原子數組
  • AtomicReferenceArray:引用類型的原子數組

直接上例子

public class AtomicIntegerArrayTest {
public static void main(String[] args) throws Exception{
AtomicIntegerArray array = new AtomicIntegerArray(10);
Thread t1 = new Thread(()->{
int index;
for(int i=1; i<100000; i++) {
index = i%10; //范圍0~9
array.incrementAndGet(index);
}
});

Thread t2 = new Thread(()->{
int index;
for(int i=1; i<100000; i++) {
index = i%10; //范圍0~9
array.decrementAndGet(index);
}
});
t1.start();
t2.start();
Thread.sleep(5 * 1000);
System.out.println(array.toString());
}
}

圖片

  • 兩個線程同時對數組對象進行加和減的操作,最終結果都是0,說明線程安全。

原子字段更新器

  • AtomicReferenceFieldUpdater
  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater

利用字段更新器,可以針對對象的某個域(Field?)進行原子操作,只能配合 volatile 修飾的字段使用,否則會出現異常。

@Data
public class AtomicReferenceFieldUpdaterTest {

private volatile int age = 10;

private int age2;

public static void main(String[] args) {
AtomicIntegerFieldUpdater integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, "age");

AtomicReferenceFieldUpdaterTest ref = new AtomicReferenceFieldUpdaterTest();
// 對volatile 的age字段+1
integerFieldUpdater.getAndIncrement(ref);
System.out.println(ref.getAge());

// 修改 非volatile的age2
integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, "age2");
integerFieldUpdater.getAndIncrement(ref);
}
}

圖片

  • 原子字段更新器只能更新volatile字段,它可以保證可見性,但是無法保證原子性。

原子累加器

原子累加器主要是用來做累加的,相關的類有LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator。

LongAdder?是jdk1.8中引入的,它的性能要比AtomicLong方式好。

LongAddr? 類是 LongAccumulator? 類的一個特例,只是 LongAccumulator? 提供了更強大的功能,可以自定義累加規則,當accumulatorFunction? 為 null 時就等價于 LongAddr。

這邊做個性能的對比例子。

public class LongAdderTest {

public static void main(String[] args) {
System.out.println("LongAdder ...........");
for (int i = 0; i < 5; i++) {
addFunc(() -> new LongAdder(), adder -> adder.increment());
}
System.out.println("AtomicLong ...........");
for (int i = 0; i < 5; i++) {
addFunc(() -> new AtomicLong(), adder -> adder.getAndIncrement());
}
}

private static <T> void addFunc(Supplier<T> adderSupplier, Consumer<T> action) {
T adder = adderSupplier.get();
long start = System.nanoTime();
List<Thread> ts = new ArrayList<>();

// 40個線程,每人累加 50
for (int i = 0; i < 40; i++) {
ts.add(new Thread(() -> {
for (int j = 0; j < 500000; j++) {
action.accept(adder);
}
}));
}
ts.forEach(t -> t.start());
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(adder + " cost:" + (end - start)/1000_000);
}
}

圖片

主要是由于LongAdder會設置多個累加單元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]... 最后將結果匯總。這樣它們在累加時操作的不同的 Cell 變量,因此減少了 CAS 重試失敗,從而提高性能。

總結

本文總結了JDK中提供的各種原子類,包括基礎原子類、原子引用類、原子數組類、原子字段更新器和原子累加器等。有時候,使用這些原子類的性能是比加鎖要高的,特別是在讀多寫少的場景下。但是,不知道大家發現沒有,所有的原子類操作對于一個共享變量執行操作是原子的,如果對于多個共享變量操作時,循環 CAS 就無法保證操作的原子性,還是老老實實加鎖吧。

責任編輯:武曉燕 來源: JAVA旭陽
相關推薦

2024-11-21 14:55:37

2020-12-11 11:11:44

原子類JavaCAS

2024-08-09 08:41:14

2021-06-07 17:12:22

線程安全Atomic

2021-02-05 11:35:03

原子類數值變量

2023-12-14 07:36:16

Java并發原子類

2023-12-01 08:54:50

Java原子類型

2019-01-04 11:18:35

獨享鎖共享鎖非公平鎖

2010-03-18 14:16:52

Java Thread

2020-01-16 14:59:32

Java鎖優化CAS

2023-10-09 08:04:52

面試CASABA

2023-10-04 11:57:20

訓練模型

2021-06-29 10:07:24

Javalong原子操作

2021-06-02 16:30:33

PolarDB原子性數據庫

2021-06-03 14:00:35

PolarDB

2022-12-06 17:28:36

Java優化CAS操作

2021-04-26 17:23:21

JavaCAS原理

2022-07-10 22:29:42

AtomicJDK項目

2021-01-15 07:41:24

CAS樂觀鎖Compare And

2024-04-22 00:00:00

CASCPU硬件
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品国产亚洲精品看不卡15 | 在线国产视频 | 一区二区三区影院 | 欧美综合自拍 | 999热精品 | 一区二区三区四区在线 | 国内精品免费久久久久软件老师 | 国产视频福利一区 | 亚洲综合色自拍一区 | 伊人精品 | 成人欧美一区二区三区黑人孕妇 | 天天操夜夜操 | 色网站在线免费观看 | 成年人在线播放 | 在线视频一区二区 | 亚洲一二三在线 | 美女黄网 | 欧美一级视频在线观看 | 国产亚洲精品久久午夜玫瑰园 | 欧美日韩国产一区二区三区 | 久久99蜜桃综合影院免费观看 | 中文字幕亚洲欧美 | 久草网在线视频 | 日韩在线一区二区三区 | 精产国产伦理一二三区 | 免费观看一级毛片 | 亚洲精品电影网在线观看 | 亚洲一区二区黄 | 久久精品视频在线免费观看 | 日本一区二区不卡 | 亚洲手机视频在线 | 免费一级欧美在线观看视频 | 欧美无乱码久久久免费午夜一区 | 大乳boobs巨大吃奶挤奶 | 免费看一级毛片 | 一区二区三区免费 | 欧美日本韩国一区二区 | 最新中文字幕在线 | 美女爽到呻吟久久久久 | 国产一区二区激情视频 | 剑来高清在线观看 |