在當今科技高速發展的時代,計算機的高速發展早已超越「摩爾定律」。在這個計算機相對廉價的時代,做爲開發者操做的機器早已不是單核處理器了,早已進入多核時代,業務早已進入並行執行;開發高併發的程序所要掌握的技能也再也不是使用沒有效率的鎖,所幸jdk1.5提供在多線程狀況下無鎖的進行原子操做,也就是這篇文章將要寫的內容。java
自JDK1.5開始提供了java.util.concurrent.atomic包,方便程序員在多線程環境下,無鎖的進行原子操做。原子變量的底層使用了處理器提供的原子指令,可是不一樣的CPU架構可能提供的原子指令不同,也有可能須要某種形式的內部鎖,因此該方法不能絕對保證線程不被阻塞。- 總的來講就是提供非阻塞的線程安全編程node
在介紹用法前先了解jdk軟件包 java.util.concurrent.atomic 中爲咱們提供了哪些原子類和方法: 程序員
(1)類摘要 編程
類 | 描述 |
---|---|
AtomicBoolean | 能夠用原子方式更新的 boolean 值。 |
AtomicInteger | 能夠用原子方式更新的 int 值。 |
AtomicIntegerArray | 能夠用原子方式更新其元素的 int 數組。 |
AtomicIntegerFieldUpdater | 基於反射的實用工具,能夠對指定類的指定 volatile int 字段進行原子更新。 |
AtomicLong | 能夠用原子方式更新的 long 值。 |
AtomicLongArray | 能夠用原子方式更新其元素的 long 數組。 |
AtomicLongFieldUpdater | 基於反射的實用工具,能夠對指定類的指定 volatile long 字段進行原子更新。 |
AtomicMarkableReference | AtomicMarkableReference 維護帶有標記位的對象引用,能夠原子方式對其進行更新。 |
AtomicReference | 能夠用原子方式更新的對象引用。 |
AtomicReferenceArray | 能夠用原子方式更新其元素的對象引用數組。 |
AtomicReferenceFieldUpdater | 基於反射的實用工具,能夠對指定類的指定 volatile 字段進行原子更新。 |
AtomicStampedReference | AtomicStampedReference 維護帶有整數「標誌」的對象引用,能夠用原子方式對其進行更新。 |
(2)經常使用方法摘要api
返回類型 | 方法 | 描述 |
---|---|---|
boolean | compareAndSet(boolean expect, boolean update) | 若是當前值 == 預期值,則以原子方式將該值設置爲給定的更新值 |
boolean | get() | 返回當前值。 |
void | set(boolean newValue) | 無條件地設置爲給定值。 |
boolean | weakCompareAndSet(boolean expect, boolean update) | 若是當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。 |
這裏介紹只列出經常使用的方法,實際中據原子類不一樣方法略有變化。如需瞭解更多的方法請查看「在線文檔-jdk-z」數組
(3)簡單使用示例 bash
示例一:原子更新基本類型類--生成序列號數據結構
public class Example1 {
private final AtomicLong sequenceNumber = new AtomicLong(0);
public long next() {
//原子增量方法,執行的是i++,因此須要在獲取一次。
sequenceNumber.getAndIncrement();
return sequenceNumber.get();
}
public void radixNext(int radix){
for (;;) {
long i = sequenceNumber.get();
// 該方法不必定執行成功,因此用無限循環來保證操做始終會執行成功一次。
boolean suc = sequenceNumber.compareAndSet(i, i + radix);
if (suc) {
break;
}
}
}
public static void main(String[] args) {
Example1 sequencer = new Example1();
//生成序列號
for (int i = 0; i < 10; i++) {
System.out.println(sequencer.next());
}
//生成自定義序列號
for (int i = 0; i < 10; i++) {
sequencer.radixNext(3);
System.out.println(sequencer.sequenceNumber.get());
}
}
}複製代碼
執行結果:多線程
1
2
3
4
5
---------------
8
11
14
17
20複製代碼
示例二:原子方式更新數組
public class Example2 {
static AtomicIntegerArray arr = new AtomicIntegerArray(10);
public static class AddThread implements Runnable{
public void run(){
for(int k=0;k<10000;k++){
// 以原子方式將索引 i 的元素加 1。
arr.getAndIncrement(k%arr.length());
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[]ts=new Thread[10];
//建立10個線程
for(int k=0;k<10;k++){
ts[k] = new Thread(new AddThread());
}
//開啓10個線程
for(int k=0;k<10;k++){
ts[k].start();
}
//等待全部線程執行完成
for(int k=0;k<10;k++){
ts[k].join();
}
//打印最終執行結果
System.out.println(arr);
}
}複製代碼
執行結果:
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]複製代碼
示例三:原子方式更新引用
public class Node {
private int val;
private volatile Node left, right;
private static final AtomicReferenceFieldUpdater leftUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
private static AtomicReferenceFieldUpdater rightUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
boolean compareAndSetLeft(Node expect, Node update) {
return leftUpdater.compareAndSet(this, expect, update);
}
public Node() {
this.left = this.right = null;
}
public Node(int val) {
this.val = val;
this.left = this.right = null;
}
public Node(Node left,Node right) {
this.left = left;
this.right = right;
}
public static void main(String[] args) {
Node node = new Node(1);
node.left = new Node(new Node(2),new Node(3));
node.right = new Node(new Node(4),new Node(5));
System.out.println(JSON.toJSON(node));
node.compareAndSetLeft(node.left,node.right);
System.out.println(JSON.toJSON(node));
}
// get and set ...
}複製代碼
執行結果:
{"val":1,"left":{"val":0,"left":{"val":2},"right":{"val":3}},"right":{"val":0,"left":{"val":4},"right":{"val":5}}}
{"val":1,"left":{"val":0,"left":{"val":4},"right":{"val":5}},"right":{"val":0,"left":{"val":4},"right":{"val":5}}}複製代碼
(4)小結
原子訪問和更新的內存效果通常遵循如下可變規則中的聲明:
關鍵源碼:
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();複製代碼
查看源碼發現Atomic包裏的類基本都是使用Unsafe實現的,Unsafe只提供瞭如下三種CAS方法。
關鍵源碼:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);複製代碼
查看方法不難發現是被native修飾的,即被Native修飾的方法在被調用時指向的是一個非java代碼的具體實現,這個實現多是其餘語言或者操做系統。這裏是藉助C來調用CPU底層指令來實現的,具體實現原理請點擊下面的「實現原理」。
原子變量類主要用做各類構造塊,用於實現非阻塞數據結構和相關的基礎結構類。compareAndSet方法不是鎖的常規替換方法。僅當對象的重要更新限定於單個變量時才應用它。
例:多線程高併發計數器
原子變量類相對於基於鎖的版本有幾個性能優點。首先,它用硬件的原生形態代替 JVM 的鎖定代碼路徑,從而在更細的粒度層次上(獨立的內存位置)進行同步,失敗的線程也能夠當即重試,而不會被掛起後從新調度。更細的粒度下降了爭用的機會,不用從新調度就能重試的能力也下降了爭用的成本。即便有少許失敗的 CAS 操做,這種方法仍然會比因爲鎖爭用形成的從新調度快得多。