我理解AtomicInteger和其他Atomic變量允許併發訪問。這個班通常在什麼情況下使用?AtomicInteger的實際應用
回答
有的AtomicInteger
兩種主要用途:
爲可以由許多線程使用同時
作爲支持compare-and-swap指令原語的原子計數器(
incrementAndGet()
等)(compareAndSet()
)來實現非阻塞算法。下面是從Brian Göetz's Java Concurrency In Practice非阻塞隨機數發生器的一個示例:
public class AtomicPseudoRandom extends PseudoRandom { private AtomicInteger seed; AtomicPseudoRandom(int seed) { this.seed = new AtomicInteger(seed); } public int nextInt(int n) { while (true) { int s = seed.get(); int nextSeed = calculateNext(s); if (seed.compareAndSet(s, nextSeed)) { int remainder = s % n; return remainder > 0 ? remainder : remainder + n; } } } ... }
正如你可以看到,它基本上作品幾乎方式與
incrementAndGet()
相同,但執行的,而不是增量任意計算(calculateNext()
) (並在返回之前處理結果)。
關鍵是它們允許安全地同時訪問和修改。它們通常用作多線程環境中的計數器 - 在引入它們之前,這必須是一個用戶編寫的類,它將同步塊中的各種方法封裝起來。
我明白了。這是在屬性或實例充當應用程序內的全局變量的情況下。或者還有其他的情況可以想到嗎? – 2011-01-27 16:13:40
我能想到的絕對最簡單的例子是增加原子操作。
隨着標準整數:
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
隨着的AtomicInteger:
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
後者是執行簡單的突變的影響(特別是計數,或獨特的索引)非常簡單的方式,而不必訴諸同步所有訪問。
更復雜的自由同步邏輯可以通過使用compareAndSet()
作爲一種類型的樂觀鎖的被採用 - 得到的電流值,在此基礎上計算結果,設置此結果IFF值仍然是用來做計算的輸入,否則重新開始 - 但計數示例非常有用,如果涉及多個線程的任何提示,我經常使用AtomicIntegers
進行計數和VM範圍的唯一生成器,因爲它們很容易與I'd幾乎認爲使用普通的ints
是不成熟的優化。
雖然幾乎總是可以實現與ints
和適當聲明相同的同步保障,中AtomicInteger
的美妙之處在於線程安全性內置到實際的對象本身,而不是你需要擔心的可能的交錯,並監視每個發生訪問int
值的方法。在調用getAndIncrement()
時,意外地違反線程安全性要比返回i++
並記住(或不)要事先獲取正確的監視器集合更困難。
感謝您的明確解釋。使用AtomicInteger比方法全部同步的類有什麼優勢?後者會被認爲是「更重」嗎? – 2011-01-27 16:14:58
例如,我有一個庫可以生成某些類的實例。這些實例中的每一個都必須具有唯一的整數ID,因爲這些實例表示要發送到服務器的命令,並且每個命令都必須具有唯一的ID。由於允許多個線程同時發送命令,因此我使用AtomicInteger來生成這些ID。另一種方法是使用某種類型的鎖和一個常規整數,但這種方法速度較慢,較不優雅。
AtomicInteger
的主要用途是當您處於多線程環境中,並且您需要在不使用的情況下對整數執行線程安全操作。原始類型int
上的分配和檢索已經是原子的,但AtomicInteger
附帶了很多在int
上不是原子的操作。
最簡單的是getAndXXX
或xXXAndGet
。例如getAndIncrement()
是一個等價於i++
的原子,它不是原子的,因爲它實際上是三個操作的縮寫:檢索,添加和分配。 compareAndSet
對於實現信號量,鎖定,鎖存器等非常有用。
使用AtomicInteger
比使用同步執行相同操作更快,更具可讀性。
一個簡單的測試:
public synchronized int incrementNotAtomic() {
return notAtomic++;
}
public void performTestNotAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
incrementNotAtomic();
}
System.out.println("Not atomic: "+(System.currentTimeMillis() - start));
}
public void performTestAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
atomic.getAndIncrement();
}
System.out.println("Atomic: "+(System.currentTimeMillis() - start));
}
我與Java 1.6的PC在3秒內的原子測試運行,而同步一個在約5.5秒運行一次。這裏的問題是同步操作(notAtomic++
)非常短。因此,與操作相比,同步的成本非常重要。
除原子性外AtomicInteger可用作Integer
的可變版本,例如Map
s作爲值。
如果你看看AtomicInteger的方法,你會注意到它們傾向於對應於整數上的常見操作。例如:
static AtomicInteger i;
// Later, in a thread
int current = i.incrementAndGet();
是這種線程安全的版本:
static int i;
// Later, in a thread
int current = ++i;
的方法映射是這樣的:
++i
是i.incrementAndGet()
i++
是i.getAndIncrement()
--i
是i.decrementAndGet()
i--
是i.getAndDecrement()
i = x
i.set(x)
是
是x = i
x = i.get()
還有其它方便的方法,以及像compareAndSet
或addAndGet
像gabuzo說,有時我使用的AtomicIntegers時,我想通過引用傳遞一個int。它是一個內置的類,具有特定於架構的代碼,因此比我可以快速編寫的任何MutableInteger更容易,更可能更優化。這就是說,這感覺就像是對班級的濫用。
您可以使用compareAndSwap(CAS)對原子整數或長整數實現非阻塞鎖。該"Tl2" Software Transactional Memory闡述這一點:
我們一個專用的版本寫鎖定每交易 存儲位置相關聯。在最簡單的形式中,版本化寫入鎖是一個 單字螺旋鎖,它使用CAS操作獲取鎖,並通過商店釋放它。由於只需要一個位來指示 鎖已被佔用,我們使用鎖字的其餘部分來保存 版本號。
它描述的是首先讀取的原子整數。將其分解爲忽略的鎖定位和版本號。試圖將CAS寫入鎖定位,並用當前版本號清除鎖定位集和下一個版本號。循環直到你成功,你就是擁有鎖的線程。通過設置鎖定位清零的當前版本號解鎖。本文描述了使用鎖中的版本號來協調線程在寫入時具有一致的一組讀取。
This article描述了處理器對比較和交換操作的硬件支持非常有效。它還聲稱:
無阻塞使用原子變量具有比低基於鎖的櫃檯更好 性能中度爭
我通常使用的AtomicInteger當我需要給IDS基於CAS的櫃檯到可以從多個線程接受或創建的對象,並且我通常將它用作我在對象的構造函數中訪問的類的靜態屬性。
在Java 8原子類已擴展了兩個有趣的功能:
- INT getAndUpdate(IntUnaryOperator updateFunction)
- INT updateAndGet(IntUnaryOperator updateFunction)
兩者都使用updateFunction到執行原子值的更新。區別在於第一個返回舊值,第二個返回新值。 updateFunction可以實現比標準更復雜的「比較和設置」操作。例如,它可以檢查原子計數器不低於零,通常它需要同步,這裏的代碼是無鎖:
public class Counter {
private final AtomicInteger number;
public Counter(int number) {
this.number = new AtomicInteger(number);
}
/** @return true if still can decrease */
public boolean dec() {
// updateAndGet(fn) executed atomically:
return number.updateAndGet(n -> (n > 0) ? n - 1 : n) > 0;
}
}
的代碼是從Java Atomic Example拍攝。
- 1. cryptico.js的實際應用
- 2. AtomicInteger的屬性
- 3. 如何使用AtomicInteger?
- 4. AtomicInteger實施和代碼複製
- 5. 零長度位域的實際應用
- 6. silverlight對AJAX的實際應用/ javascript
- 7. 按位操作的實際應用
- 8. 類破壞器的實際應用
- 9. AtomicInteger和Math.max
- 10. AtomicInteger遞增
- 11. 實際使用
- 12. 什麼是實際應用過濾器?
- 13. Java AtomicInteger getAndIncrement()和compareAndSet
- 14. AtomicInteger vs synchronized getters/setters
- 15. C#實際使用
- 16. 實際使用IBOutletColletion
- 17. jQuery.get() - 實際用途?
- 18. 實際使用dynamic_cast?
- 19. 是實際使用的xsi:schemaLocation?
- 20. Dispatcher.DisableProcessing的實際用法?
- 21. 線程的實際用途
- 22. BufferManager的實際用例
- 23. 實際使用IdentityHashMap的
- 24. Powershell:$ ^和$$的實際用法?
- 25. MethodRental Class的實際用法?
- 26. 獲取ngResource的實際響應保存()
- 27. MVC ||的實際應用何時使用或不使用MVC
- 28. AtomicInteger用於有限序列生成
- 29. 安全地使用AtomicInteger首先檢查
- 30. AtomicInteger比同步更慢
我想我明白了第一次使用。這是爲了確保在再次訪問屬性之前計數器已經增加。正確?你能舉一個簡短的例子來介紹第二次使用嗎? – 2011-01-27 16:11:54
您對第一次使用的理解是真實的 - 它只是確保如果另一個線程修改了「讀取」和「寫入該值+ 1」操作之間的計數器,則會檢測到該值,而不是覆蓋舊更新(避免「丟失更新「問題)。這實際上是`compareAndSet`的一個特例 - 如果舊值爲`2`,則該類實際調用`compareAndSet(2,3)` - 因此如果另一個線程同時修改了該值,則增量方法將有效地重新啓動從一開始就。 – 2011-01-27 16:22:20
「餘數> 0→餘數:餘數+ n;」在這個表達式中是否有一個當n爲0時向n添加餘數的理由? – sandeepkunkunuru 2016-02-19 16:20:43