併發集合(九)使用原子 arrays
宣告:本文是《Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González 譯者:鄭玉婷 校對:黃庭
當你實現一個多個執行緒共享一個或者多個物件的併發應用時,你就要使用像鎖或者同步關鍵詞(例如synchronized)來對他們的屬性的訪問進行保護,來避免併發造成的資料不一致的錯誤。
但是這些機制會有以下一些缺點:
死鎖(dead lock):例如:當一個執行緒等待一個鎖的時候,會被阻塞,而這個鎖被其他執行緒佔用並且永不釋放。這種情況就是死鎖,程式在這種情況下永遠都不會往下執行。
即使只有一個執行緒在訪問共享物件,它也要執行必要的獲取鎖和釋放鎖的程式碼。
CAS(compare-and-swap)操作為併發操作物件的提供更好的效能,CAS操作通過以下3個步驟來實現對變數值得修改:
- 獲取當前記憶體中的變數的值
- 用一個新的臨時變數(temporal variable)儲存改變後的新值
- 如果當前記憶體中的值等於變數的舊值,則將新值賦值到當前變數;否則不進行任何操作
對於這個機制,你不需要使用任何同步機制,這樣你就避免了 deadlocks,也獲得了更好的效能。這種機制能保證多個併發執行緒對一個共享變數操作做到最終一致。
Java 在原子類中實現了CAS機制。這些類提供了compareAndSet() 方法;這個方法是CAS操作的實現和其他方法的基礎。
Java 中還引入了原子Array,用來實現Integer型別和Long型別陣列的操作。在這個指南里,你將要學習如何使用AtomicIntegerArray 類來操作原子 arrays。
指南中的例子是在Eclipse IDE下面實現的,你也可以使用其他IDE例如NetBeans來實現:
那要怎麼做呢….
按照這些步驟來實現下面的例子:
//1.建立一個類,名為 Incrementer,並實現 Runnable 介面。 public class Incrementer implements Runnable { //2.宣告一個私有 AtomicIntegerArray 屬性,名為 vector,用來儲存一個整數 array。 private AtomicIntegerArray vector; //3.實現類的建構函式,初始化它的屬性值。 public Incrementer(AtomicIntegerArray vector) { this.vector=vector; } //4.實現 run() 方法。使用 getAndIncrement() 方操作array裡的所有元素。 @Override public void run() { for (int i=0; i<vector.length(); i++){ vector.getAndIncrement(i); } } //5.建立一個類,名為 Decrementer,並實現 Runnable 介面。 public class Decrementer implements Runnable { //6.宣告一個私有 AtomicIntegerArray 屬性,名為 vector,用來儲存一個整數 array。 private AtomicIntegerArray vector; //7.實現類的建構函式,初始化它的屬性值。 public Decrementer(AtomicIntegerArray vector) { this.vector=vector; } //8.實現 run() 方法。使用 getAndDecrement() 方法操作array裡的所有元素。 @Override public void run() { for (int i=0; i<vector.length(); i++) { vector.getAndDecrement(i); } } //9.我們建立一個示例來進行示範,建立一個類,名為 Main 並新增 main()方法。 public class Main { public static void main(String[] args) { //10.宣告一個常量,名為 THREADS,分配它的值為 100。建立一個有1,000個元素的 AtomicIntegerArray 物件。 final int THREADS=100; AtomicIntegerArray vector=new AtomicIntegerArray(1000); //11. 建立一個 Incrementer 任務來操作之前建立的原子 array。 Incrementer incrementer=new Incrementer(vector); //12.建立一個 Decrementer 任務來操作之前建立的原子 array。 Decrementer decrementer=new Decrementer(vector); //13.建立2個array 分別儲存 100 個Thread 物件。 Thread threadIncrementer[]=new Thread[THREADS]; Thread threadDecrementer[]=new Thread[THREADS]; //14.建立並執行 100 個執行緒來執行 Incrementer 任務和另外 100 個執行緒來執行 Decrementer 任務。把執行緒儲存入之前建立的arrays內。 for (int i=0; i<THREADS; i++) { threadIncrementer[i]=new Thread(incrementer); threadDecrementer[i]=new Thread(decrementer); threadIncrementer[i].start(); threadDecrementer[i].start(); } //15.使用 join() 方法來等待執行緒的完結。 for (int i=0; i<100; i++) { try { threadIncrementer[i].join(); threadDecrementer[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } //16.把原子array裡非0的元素寫入操控臺。使用 get() 方法來獲取原子 array 元素。 for (int i=0; i<vector.length(); i++) { if (vector.get(i)!=0) { System.out.println("Vector["+i+"] : "+vector.get(i)); } } //17.在操控臺寫個資訊表明例子結束。 System.out.println("Main: End of the example");
它是怎麼工作的…
在這個例子裡,你實現了2個不同的任務來操作 AtomicIntegerArray 物件:
Incrementer task: 這個類使用getAndIncrement()方法array裡的全部元素 +1
Decrementer task: 這個類使用getAndDecrement()方法array裡的全部元素 -1
在 Main 類,你建立了有1000個元素的 AtomicIntegerArray,然後你執行了100次 Incrementer 和100次 Decrementer 任務。在任務結束後,如果沒有出現任何資料不一致錯誤,那麼array的全部元素的值都為0。如果你執行這個任務,由於全部元素都是0,你只會看到程式在操控臺只寫了結束資訊。
更多…
如今,Java僅提供了另一個原子 array類。它是 AtomicLongArray 類,與 IntegerAtomicArray 類提供了相同的方法。
這些類的一些其他有趣的方法有:
get(int i): 返回array中第i個位置上的值
set(int I, int newValue): 設定array中第i個位置上的值為newValue
參見
第六章,併發集:使用原子變數