實現自定義原子物件
實現自定義原子物件
Java版本5引入原子變數,為單變數提供原子操作。當執行緒執行包含原子變數的操作時,類實現檢查操作是否以原子方式完成。
本節將學習如何擴充套件原子物件,實現遵循原子物件機制的兩個操作,確保所有操作都在一個步驟中完成。
準備工作
本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。
實現過程
通過如下步驟實現範例:
-
建立名為ParkingCounter的類,繼承AtomicInteger類:
public class ParkingCounter extends AtomicInteger{
-
宣告名為maxNumber的私有int屬性儲存進入停車場的汽車的最大數量:
private final int maxNumber;
-
實現類建構函式,初始化屬性:
public ParkingCounter(int maxNumber){ set(0); this.maxNumber=maxNumber; }
-
實現carIn()方法,如果值小於設定的最大值,此方法遞增汽車計數器。構造一個無限迴圈,使用get()方法獲取內部計數器的值:
public
-
如果值與maxNumber屬性值相同,計數器無法再遞增(停車場滿載,汽車無法進入)。這種請款下,此方法返回false值:
if (value==maxNumber) { System.out.printf("ParkingCounter: The parking lot is full.\n"); return false;
-
否則,增加值且使用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; } } } }
-
實現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; } } } } }
-
建立名為Sensor1的類,實現Runnable介面:
public class Sensor1 implements Runnable {
-
宣告名為counter的私有ParkingCounter屬性:
private final ParkingCounter counter;
-
實現類建構函式,初始化屬性:
public Sensor1(ParkingCounter counter) { this.counter=counter; }
-
實現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(); } }
-
建立名為Sensor2的類,實現Runnable介面:
public class Sensor2 implements Runnable {
-
宣告名為counter的私有ParkingCounter屬性:
private ParkingCounter counter;
-
實現類建構函式,初始化屬性:
public Sensor2(ParkingCounter counter) { this.counter=counter; }
-
實現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(); } }
-
通過建立名為Main的類,新增main()方法,實現本範例主類:
public class Main { public static void main(String[] args) throws Exception{
-
建立名為counter的ParkingCounter物件:
ParkingCounter counter=new ParkingCounter(5);
-
建立、載入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();
-
等待兩個任務結束:
thread1.join(); thread2.join();
-
輸出計數器的實際值到控制檯:
System.out.printf("Main: Number of cars: %d\n",counter.get());
-
輸出指明程式結束的資訊到控制檯:
System.out.printf("Main: End of the program.\n"); } }
工作原理
ParkingCounter類繼承AtomicInteger類,包含兩個原子操作:carIn()和carOut()。本範例模擬控制進入停車場汽車數量的系統,停車場可以容納多輛車,用maxNumber屬性表示。
carIn()操作將停車場中實際的汽車數量和最大值不計較,如果相等,汽車無法進入停車場且此方法返回false值。否則,使用如下的原子操作結構:
- 在區域性變數中獲取原子物件值。
- 在不同的變數中儲存新值。
- 使用compareAndSet()方法試圖將舊值替換成新值。如果此方法返回true,則作為引數傳送的舊值是變數值,因此方法改變值。因為carIn()方法返回true,所以此操作以原子方式進行。如果compareAndSet()方法返回false,則作為引數傳送的舊值不是變數值(其它執行緒修改了值),因此此操作不能以原子方式進行。操作再次開始,直到以原子方式完成。
carOut()方法類似於carIn()方法,還實現了兩個Runnable物件,分別使用carIn()和carOut()方法來模擬停車動作。當執行本範例時,能夠看到停車場永遠不會超過汽車數量的最大值。
更多關注
- 第七章“併發集合”中的“使用原子變數”小節