杭州建立網(wǎng)站長沙企業(yè)網(wǎng)站建設(shè)報價
目錄
1. 什么是原子操作
1.1?原子類的作用
1.2?原子類的常見操作
原子類的使用注意事項
并發(fā)編程是現(xiàn)代計算機(jī)應(yīng)用中不可或缺的一部分,而在并發(fā)編程中,處理共享資源的并發(fā)訪問是一個重要的問題。為了避免多線程訪問共享資源時出現(xiàn)競態(tài)條件(Race Condition)等問題,Java提供了一組原子類(Atomic Classes)來支持線程安全的操作。
1. 什么是原子操作
在并發(fā)編程中,原子操作是不可被中斷的一個或一系列操作,要么全部執(zhí)行成功,要么全部不執(zhí)行,不會出現(xiàn)部分執(zhí)行的情況。原子操作能夠保證在多線程環(huán)境下,對共享資源的操作不會相互干擾,從而確保數(shù)據(jù)的一致性和可靠性。
1.1?原子類的作用
Java提供了一組原子類,位于java.util.concurrent.atomic
包中,用于在多線程環(huán)境下進(jìn)行原子操作。這些原子類利用底層的硬件支持或自旋鎖等機(jī)制來實現(xiàn)線程安全的操作,避免了顯式地使用synchronized
關(guān)鍵字等鎖機(jī)制,從而提高了并發(fā)性能。
原子類的作用主要有以下幾點:
-
提供線程安全的操作: 原子類提供了一些常見的操作,如讀取、更新、比較交換等,這些操作在執(zhí)行時不會受到其他線程的干擾,從而確保數(shù)據(jù)的一致性。
-
避免競態(tài)條件: 使用原子類可以有效地避免多線程環(huán)境下的競態(tài)條件問題,例如多個線程同時對同一個變量進(jìn)行操作,可能導(dǎo)致不可預(yù)測的結(jié)果。
-
提高性能: 原子類在實現(xiàn)上利用了一些底層的技術(shù),避免了傳統(tǒng)鎖機(jī)制的開銷,因此在某些情況下可以提供更好的性能。
1.2?原子類的常見操作
1. AtomicBoolean
AtomicBoolean
類提供了原子的布爾值操作,支持原子的設(shè)置和獲取操作。
AtomicBoolean atomicBoolean = new AtomicBoolean(true);boolean currentValue = atomicBoolean.get(); // 獲取當(dāng)前值
boolean updatedValue = atomicBoolean.compareAndSet(true, false); // 如果當(dāng)前值為true,則設(shè)置為false
?
2. AtomicInteger 和 AtomicLong
AtomicInteger
和AtomicLong
分別提供了原子的整數(shù)和長整數(shù)操作,包括增加、減少、獲取等操作。
AtomicInteger atomicInt = new AtomicInteger(0);int currentValue = atomicInt.get(); // 獲取當(dāng)前值
int newValue = atomicInt.incrementAndGet(); // 增加1并返回新值
int updatedValue = atomicInt.addAndGet(5); // 增加5并返回新值
3. AtomicReference
AtomicReference
允許在原子級別上操作引用類型的數(shù)據(jù)。它提供了get
、set
和compareAndSet
等方法。
AtomicReference<String> atomicRef = new AtomicReference<>("initial value");String currentValue = atomicRef.get(); // 獲取當(dāng)前值
boolean updated = atomicRef.compareAndSet("initial value", "new value"); // 如果當(dāng)前值為"initial value",則設(shè)置為"new value"
?
4. AtomicStampedReference
AtomicStampedReference
是對AtomicReference
的擴(kuò)展,它還包含一個時間戳,用于解決ABA問題(即一個值被修改為另一個值,然后又被修改回原來的值,但是在這之間可能發(fā)生了其他的變化)。
AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>("initial value", 0);int currentStamp = atomicStampedRef.getStamp(); // 獲取當(dāng)前時間戳
String currentValue = atomicStampedRef.getReference(); // 獲取當(dāng)前值
boolean updated = atomicStampedRef.compareAndSet("initial value", "new value", 0, 1); // 如果當(dāng)前值為"initial value"且時間戳為0,則設(shè)置為"new value"和時間戳為1
5. AtomicArray
AtomicArray
類允許在原子級別上操作數(shù)組元素,提供了針對數(shù)組元素的原子更新操作。
AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(5);int currentValue = atomicIntArray.get(2); // 獲取索引為2的元素值
atomicIntArray.set(3, 10); // 設(shè)置索引為3的元素值為10
int updatedValue = atomicIntArray.getAndAdd(1, 5); // 增加索引為1的元素值,并返回舊值
6. AtomicReferenceFieldUpdater
AtomicReferenceFieldUpdater
是Java中的一個工具類,用于進(jìn)行原子更新類的引用類型字段的操作。它允許您在不使用鎖的情況下對指定的引用字段進(jìn)行原子操作,類似于AtomicFieldUpdater
,但專門用于引用類型的字段。AtomicReferenceFieldUpdater
主要用于確保在多線程環(huán)境下對引用字段的操作是線程安全的,并且可以提供更好的性能。
AtomicReferenceFieldUpdater
適用于以下場景:
-
當(dāng)您需要在不使用鎖的情況下對特定類的引用字段進(jìn)行原子更新時。
-
當(dāng)引用字段的訪問修飾符是
volatile
,以確保多線程之間的可見性。 -
當(dāng)您希望在多個實例之間共享原子更新引用字段的功能,而不是整個對象。
要使用AtomicReferenceFieldUpdater
,首先需要創(chuàng)建一個AtomicReferenceFieldUpdater
的實例。這可以通過調(diào)用AtomicReferenceFieldUpdater.newUpdater(Class<T> tclass, Class<V> vclass, String fieldName)
方法來實現(xiàn),其中:
tclass
是包含字段的類的Class
對象。vclass
是字段的引用類型的Class
對象。fieldName
是要進(jìn)行原子操作的引用字段的名稱。
以下是一個示例代碼片段,演示如何創(chuàng)建和使用AtomicReferenceFieldUpdater
實例:
public class AtomicReferenceFieldUpdaterExample {public static class Student {public volatile String name;}public static void main(String[] args) {Student student = new Student();AtomicReferenceFieldUpdater<Student, String> updater =AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");updater.set(student, "Alice"); // 原子地設(shè)置name字段為"Alice"String updatedName = updater.get(student); // 原子地獲取name字段的值System.out.println("Updated Name: " + updatedName);}
}
AtomicReferenceFieldUpdater
提供了一系列的原子操作方法,用于對指定引用字段進(jìn)行原子更新。這些方法包括:
-
boolean compareAndSet(T obj, V expect, V update)
:如果當(dāng)前值等于expect
,則將字段更新為update
,返回是否更新成功。 -
V getAndSet(T obj, V newValue)
:將字段更新為newValue
,并返回之前的值。 -
V getAndUpdate(T obj, UnaryOperator<V> updateFunction)
:使用給定的更新函數(shù)更新字段,并返回更新前的值。 -
V updateAndGet(T obj, UnaryOperator<V> updateFunction)
:使用給定的更新函數(shù)更新字段,并返回更新后的值。 -
V getAndAccumulate(T obj, V x, BinaryOperator<V> accumulatorFunction)
:使用給定的累加函數(shù)將字段與x
進(jìn)行累加操作,并返回更新前的值。 -
V accumulateAndGet(T obj, V x, BinaryOperator<V> accumulatorFunction)
:使用給定的累加函數(shù)將字段與x
進(jìn)行累加操作,并返回更新后的值。
7.?LongAdder
LongAdder
是Java并發(fā)包中提供的一種用于高并發(fā)場景下對long類型進(jìn)行累加操作的工具類。與傳統(tǒng)的AtomicLong
相比,LongAdder
在高并發(fā)情況下通常能夠提供更好的性能,因為它采用了一種分段的方式來減少競爭。LongAdder
的引入主要是為了應(yīng)對高并發(fā)累加操作的性能瓶頸,特別是在多核處理器上。
LongAdder
在高并發(fā)場景下的主要優(yōu)勢在于分段累加,以及對熱點數(shù)據(jù)的分離處理。傳統(tǒng)的AtomicLong
在高并發(fā)情況下可能會因為多線程之間的競爭而導(dǎo)致性能下降,而LongAdder
通過將累加操作分成多個段,每個段維護(hù)一個計數(shù)器,從而減少了競爭。
另外,LongAdder
還引入了一種稱為“分離器”(Cell)的機(jī)制。分離器是計數(shù)器的基本單元,每個線程在累加時會選擇一個分離器進(jìn)行操作,這避免了多線程頻繁地競爭同一個計數(shù)器,從而減少了競爭帶來的開銷。
要使用LongAdder
,只需要簡單地創(chuàng)建一個LongAdder
的實例即可:
LongAdder longAdder = new LongAdder();
?
LongAdder
提供了一些常用的方法來進(jìn)行累加操作:
-
void add(long x)
:將指定的值添加到計數(shù)器中。 -
void increment()
:將計數(shù)器增加1。 -
void decrement()
:將計數(shù)器減少1。 -
long sum()
:返回當(dāng)前計數(shù)器的總和。 -
void reset()
:將計數(shù)器重置為0。 -
void addThenReset(long x)
:將指定的值添加到計數(shù)器中,然后將計數(shù)器重置為0。
原子類的使用注意事項
-
性能考慮: 雖然原子類可以提供一定程度的性能優(yōu)勢,但并不是適用于所有情況。在高并發(fā)場景下,考慮使用原子類;而在低并發(fā)、性能要求不高的情況下,可能傳統(tǒng)的同步機(jī)制更加合適。
-
CAS操作的限制: 原子類的底層實現(xiàn)主要依賴于CAS(Compare-And-Swap)操作,這是一種樂觀鎖機(jī)制。然而,CAS操作可能會在競爭激烈的情況下導(dǎo)致自旋等待,影響性能。
-
ABA問題: 原子類的CAS操作可能存在ABA問題,即一個值從A變?yōu)锽,然后又從B變?yōu)锳,這時CAS操作可能會錯誤地認(rèn)為值沒有發(fā)生變化??梢允褂?code>AtomicStampedReference來解決此問題。
-
復(fù)合操作的原子性: 原子類的單個操作是原子的,但多個操作的組合并不一定是原子的。例如,
AtomicInteger
的incrementAndGet
操作是原子的,但在使用時仍然需要考慮復(fù)合操作的原子性。 -
適用范圍: 原子類適用于簡單的原子操作,但并不適用于復(fù)雜的業(yè)務(wù)邏輯。對于復(fù)雜的操作,可能需要使用鎖等更高級的同步機(jī)制來確保線程安全。