閱讀597 返回首頁    go 阿裏雲 go 技術社區[雲棲]


並發集合(九)使用原子 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來實現:

那要怎麼做呢….

按照這些步驟來實現下麵的例子:

01 //1.創建一個類,名為 Incrementer,並實現 Runnable 接口。
02 public class Incrementer implements Runnable {
03  
04 //2.聲明一個私有 AtomicIntegerArray 屬性,名為 vector,用來儲存一個整數 array。
05 private AtomicIntegerArray vector;
06  
07 //3.實現類的構造函數,初始化它的屬性值。
08 public Incrementer(AtomicIntegerArray vector) {
09          this.vector=vector;
10 }
11  
12 //4.實現 run() 方法。使用 getAndIncrement() 方操作array裏的所有元素。
13 @Override
14 public void run() {
15          for (int i=0; i<vector.length(); i++){
16                  vector.getAndIncrement(i);
17          }
18 }
19  
20 //5.創建一個類,名為 Decrementer,並實現 Runnable 接口。
21 public class Decrementer implements Runnable {
22  
23 //6.聲明一個私有 AtomicIntegerArray 屬性,名為 vector,用來儲存一個整數 array。
24 private AtomicIntegerArray vector;
25  
26 //7.實現類的構造函數,初始化它的屬性值。
27 public Decrementer(AtomicIntegerArray vector) {
28          this.vector=vector;
29 }
30  
31 //8.實現 run() 方法。使用 getAndDecrement() 方法操作array裏的所有元素。
32 @Override
33 public void run() {
34          for (int i=0; i<vector.length(); i++) {
35          vector.getAndDecrement(i);
36          }
37 }
38  
39 //9.我們創建一個示例來進行示範,創建一個類,名為 Main 並添加 main()方法。
40 public class Main {
41          public static void main(String[] args) {
42  
43 //10.聲明一個常量,名為 THREADS,分配它的值為 100。創建一個有1,000個元素的 AtomicIntegerArray 對象。
44 final int THREADS=100;
45 AtomicIntegerArray vector=new AtomicIntegerArray(1000);
46  
47 //11. 創建一個 Incrementer 任務來操作之前創建的原子 array。
48 Incrementer incrementer=new Incrementer(vector);
49  
50 //12.創建一個 Decrementer 任務來操作之前創建的原子 array。
51 Decrementer decrementer=new Decrementer(vector);
52  
53 //13.創建2個array 分別存儲 100 個Thread 對象。
54 Thread threadIncrementer[]=new Thread[THREADS];
55 Thread threadDecrementer[]=new Thread[THREADS];
56  
57 //14.創建並運行 100 個線程來執行 Incrementer 任務和另外 100 個線程來執行 Decrementer 任務。把線程儲存入之前創建的arrays內。
58 for (int i=0; i<THREADS; i++) {
59          threadIncrementer[i]=new Thread(incrementer);
60          threadDecrementer[i]=new Thread(decrementer);
61  
62          threadIncrementer[i].start();
63          threadDecrementer[i].start();
64 }
65 //15.使用 join() 方法來等待線程的完結。
66 for (int i=0; i<100; i++) {
67          try {
68                  threadIncrementer[i].join();
69                  threadDecrementer[i].join();
70          catch (InterruptedException e) {
71                  e.printStackTrace();
72          }
73 }
74 //16.把原子array裏非0的元素寫入操控台。使用 get() 方法來獲取原子 array 元素。
75 for (int i=0; i<vector.length(); i++) {
76          if (vector.get(i)!=0) {
77                  System.out.println("Vector["+i+"] : "+vector.get(i));
78          }
79 }
80  
81 //17.在操控台寫個信息表明例子結束。
82 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

參見
第六章,並發集:使用原子變量

文章轉自 並發編程網-ifeve.com

最後更新:2017-05-23 17:32:17

  上一篇:go  一個api的 rt 大漲問題排查
  下一篇:go  並發網係列文章集