實現全局自增id最簡單有效的方式是什麼?java.util.concurrent.atomic
包定義了一些常見類型的原子變量。這些原子變量爲咱們提供了一種操做單一變量無鎖(lock-free)的線程安全(thread-safe)方式。實際上該包下面的類爲咱們提供了相似volatile
變量的特性,同時還提供了諸如boolean compareAndSet(expectedValue, updateValue)
的功能。不使用鎖實現線程安全聽起來彷佛很難以想象,這實際上是經過CPU的compare and swap指令實現的,因爲硬件指令支持固然不須要加鎖了。java
先不去討論這些細節,咱們來看一下原子變量的用法。一個典型的用法是可使用原子變量輕鬆實現全局自增id,就像下面這樣:git
// 線程安全的序列id生成器 class Sequencer { private final AtomicLong sequenceNumber = new AtomicLong(0); public long next() { return sequenceNumber.getAndIncrement(); } }
上述代碼利用AtomicLong建立了一個Sequencer類,不斷調用該類的next()方法就能夠獲得線程安全的自增id,用起來很是簡單直觀。下面咱們給出每種原子變量類型的用法說明。github
AtomicInteger和AtomicLong分別表明原子類型的整型和長整型,這兩個類提供十分類似的功能,僅僅是位寬不一樣。如上例所示,原子整型可用於多線程下全局自增id,除此以外還提供了原子比較-賦值等操做,諸如compareAndSet(expect, update)
, decrementAndGet()
,getAndDecrement()
,getAndSet(newValue)
等等,更全面的接口描述可參考JDK文檔。須要提醒的是這些函數都是經過原子CPU指令實現,執行效率較高。數組
原子整型看似跟普通整型(Integer, Long)類型類似,但不能使用原子整型替代普通整型,由於原子整型是可變的,而普通整型不可變。因爲這個緣由,使用原子整型做爲Map的key並非個好主意。安全
你可能會想固然的覺得應該有AtomicFloat和AtomicDouble,遺憾的是類庫裏並無這兩個類型,AtomicByte和AtomicShort也沒有。若是須要替代方案是使用AtomicInteger和AtomicLong。可經過Float.floatToRawIntBits(float)
和Float.intBitsToFloat(int)
將Float存儲到AtomicInteger中,相似的Double類型也能夠存儲到AtomicLong中。markdown
AtomicReference用於存放一個能夠原子更新的對象引用。該類包含get()
, set()
, compareAndSet()
, getAndSet()
等原子方法來獲取和更新其表明的對象引用。多線程
atomic包下面有三種原子數組:AtomicIntegerArray
, AtomicLongArra
, AtomicReferenceArray
,分別表明整型、長整型和引用類型的原子數組。原子數組使得咱們能夠線程安全的方式去修改和訪問數組裏的單個元素。簡單示例以下:函數
// 原子數組示例 AtomicLongArray longArray = new AtomicLongArray(10);// 建立長度爲10的原子數組 longArray.set(1, 100); long v = longArray.getAndIncrement(1); AtomicReferenceArray<String> referenceArray = new AtomicReferenceArray<>(16); referenceArray.set(3, "love"); referenceArray.compareAndSet(3, "love", "you");
簡單來講原子數組就是一種支持線程安全的數組,仍然具備數組「定長」的性質,若是訪問元素超過了數組的長度,將會拋出IndexOutOfBoundsException
。你可能已經想到了,可使用線程安全的容器來避免容量不足,咱們會在後續章節介紹。atom
線程安全是指多線程訪問是時,不管線程的調度策略是什麼,程序可以正確的執行。致使線程不安全的一個緣由是狀態不一致,若是線程A修改了某個共享變量(好比給id++),而線程B沒有及時知道,就會致使B在錯誤的狀態上執行,結果的正確性也就沒法保證。原子變量爲咱們提供了一種保證單個狀態一致的簡單方式,一個線程修改了原子變量,另外的線程當即就能看到,這比經過鎖實現的方式效率要高;若是要同時保證多個變量狀態一致,就只能使用鎖了。線程