1. 程式人生 > >併發集合(九)使用原子 arrays

併發集合(九)使用原子 arrays

宣告:本文是《Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González 譯者:鄭玉婷 校對:黃庭

當你實現一個多個執行緒共享一個或者多個物件的併發應用時,你就要使用像鎖或者同步關鍵詞(例如synchronized)來對他們的屬性的訪問進行保護,來避免併發造成的資料不一致的錯誤。

但是這些機制會有以下一些缺點:
死鎖(dead lock):例如:當一個執行緒等待一個鎖的時候,會被阻塞,而這個鎖被其他執行緒佔用並且永不釋放。這種情況就是死鎖,程式在這種情況下永遠都不會往下執行。

即使只有一個執行緒在訪問共享物件,它也要執行必要的獲取鎖和釋放鎖的程式碼。

CAS(compare-and-swap)操作為併發操作物件的提供更好的效能,CAS操作通過以下3個步驟來實現對變數值得修改:

  1. 獲取當前記憶體中的變數的值
  2. 用一個新的臨時變數(temporal variable)儲存改變後的新值
  3. 如果當前記憶體中的值等於變數的舊值,則將新值賦值到當前變數;否則不進行任何操作

對於這個機制,你不需要使用任何同步機制,這樣你就避免了 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

參見
第六章,併發集:使用原子變數