定製併發類(十一)實現自定義的原子物件
宣告:本文是《 Java 7 Concurrency Cookbook 》的第七章,作者: Javier Fernández González 譯者:許巧輝 校對:方騰飛
實現你自己的原子(atomic)物件
Java版本5中引入原子變數,並提供對單個變數的原子操作。當一個執行緒在原子變數上執行操作時,這個類的實現包含一種機制用來檢查這個操作在一個步驟內完成。基本上,這個操作是先獲取變數的值,然後在本地變數中改變這個值,最後嘗試將舊值變成這個新值。如果舊值仍然是相同的,它將改變成新值,否則,這個方法重新開始這個操作。(校對注:這段話描述了CAS的實現原理 )
在這個指南中,你將學習如何繼承一個原子物件和如何實現遵從原子物件機制的兩個操作,來保證所有的操作在一個步驟內完成。
準備工作…
這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。
如何做…
按以下步驟來實現的這個例子:
1.建立ParkingCounter類,並指定它繼承AtomicInteger類。
public class ParkingCounter extends AtomicInteger {
2.宣告一個私有的、int型別的屬性maxNumber,用來儲存停車場允許停放汽車的最大數量。
private int maxNumber;
3.實現這個類的構造器,並初始化它的屬性。
public ParkingCounter(int maxNumber){ set(0); this.maxNumber=maxNumber; }
4.實現carIn()方法。這個方法增加車的計數器,如果它小於設定的最大數。構建一個無限迴圈,並使用get()方法獲取內部計數器的值。
public boolean carIn() { for (;;) { int value=get();
5.如果計數器的值等於最maxNumber屬性值,這個計數器不能再增加(停車場已滿,其他車不能再進入)。這個方法返回false值。
if (value==maxNumber) { System.out.printf("ParkingCounter: The parking lot is full.\n"); return false;
6.否則,增加這個值,並compareAndSet()方法將舊值變成新值。如果這個方法返回false值,說明計數器沒有增加,所以你必須重新開始這個迴圈。如果這個方法返回true值,它意味著改變操作成功,然後你返回了true值。
} else { int newValue=value+1; boolean changed=compareAndSet(value,newValue); if (changed) { System.out.printf("ParkingCounter: A car has entered.\n"); return true; } } } }
7.實現carOut()方法。這個方法減少車的計數器值,如果它大於0。構建一個無限迴圈,並使用get()方法獲取內部的計數器的值。
public boolean carOut() { for (;;) { int value=get(); if (value==0) { System.out.printf("ParkingCounter: The parking lot is empty.\n"); return false; } else { int newValue=value-1; boolean changed=compareAndSet(value,newValue); if (changed) { System.out.printf("ParkingCounter: A car has gone out.\n"); return true; } } } }
8.建立一個實現Runnable介面的Sensor1類。
public class Sensor1 implements Runnable {
9.宣告一個私有的、ParkingCounter型別的屬性counter。
private ParkingCounter counter;
10.實現這個類的構造器,並初始化它的屬性。
public Sensor1(ParkingCounter counter) { this.counter=counter; }
11.實現run()方法。呼叫幾次carIn()和carOut()操作。
@Override public void run() { counter.carIn(); counter.carIn(); counter.carIn(); counter.carIn(); counter.carOut(); counter.carOut(); counter.carOut(); counter.carIn(); counter.carIn(); counter.carIn(); }
12.建立一個實現了Runnable介面的Sensor2類。
public class Sensor2 implements Runnable {
13.宣告一個私有的、ParkingCounter型別的屬性counter。
private ParkingCounter counter;
14.實現這個類的構造器,並初始化它的屬性。
public Sensor2(ParkingCounter counter) { this.counter=counter; }
15.實現run()方法。呼叫幾次carIn()和carOut()操作。
@Override public void run() { counter.carIn(); counter.carOut(); counter.carOut(); counter.carIn(); counter.carIn(); counter.carIn(); counter.carIn(); counter.carIn(); counter.carIn(); }
16.實現這個例子的主類,通過實現Main()類,並實現main()方法。
public class Main { public static void main(String[] args) throws Exception {
17.建立一個ParkingCounter物件,名為counter。
ParkingCounter counter=new ParkingCounter(5);
18.建立和啟動一個Sensor1任務和一個Sensor2任務。
Sensor1 sensor1=new Sensor1(counter); Sensor2 sensor2=new Sensor2(counter); Thread thread1=new Thread(sensor1); Thread thread2=new Thread(sensor2); thread1.start(); thread2.start();
19.等待這兩個任務的結束。
thread1.join(); thread2.join();
20.將計數器的實際值寫入到控制檯。
System.out.printf("Main: Number of cars: %d\n",counter.get());
21.寫入一條資訊到控制檯表明程式的結束。
System.out.printf("Main: End of the program.\n");
它是如何工作的…
繼承AtomicInteger類的ParkingCounter類有兩個原子操作,carIn()和carOunt()。這個例子模擬一個系統來控制停車場內的汽車數。這個停車場可容納的汽車數用maxNumber屬性表示。
carIn()操作將實際汽車數與停車場(可容納的汽車數)的最大值進行比較。如果它們相等,這輛汽車不能進行停車場並返回false值。否則,它使用以下的原子操作結構:
- 用一個本地變數獲取原子物件的值。
- 用一個不同的變數來儲存新值。
- 使用compareAndSet()方法嘗試將舊值替換成新值。如果這個方法返回true,作為引數傳入的舊值是這個變數的值,因此,它使值變化。隨著carIn()方法返回true值,這個操作將以原子方式完成。如果compareAndSet()方法返回false值,作為引數傳入的舊值不是這個變數的值(其他執行緒已修改它),所以這個操作不能以原子方式完成。這個操作將重新開始,直到它可以以原子方式完成。
carOut()方法與carIn()方法類似。你已實現兩個Runnable物件,使用carIn()和carOut()來模擬停車的活動。當你執行這個程式,你可以看出停車場沒有克服汽車在停車場的最大值。
參見