1. 程式人生 > >實現自定義原子物件

實現自定義原子物件

Java 9併發程式設計指南 目錄

實現自定義原子物件

Java版本5引入原子變數,為單變數提供原子操作。當執行緒執行包含原子變數的操作時,類實現檢查操作是否以原子方式完成。

本節將學習如何擴充套件原子物件,實現遵循原子物件機制的兩個操作,確保所有操作都在一個步驟中完成。

準備工作

本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。

實現過程

通過如下步驟實現範例:

  1. 建立名為ParkingCounter的類,繼承AtomicInteger類:

    public class ParkingCounter  extends AtomicInteger{
    
  2. 宣告名為maxNumber的私有int屬性儲存進入停車場的汽車的最大數量:

    	private final 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. 建立名為Sensor1的類,實現Runnable介面:

    public class Sensor1 implements Runnable {
    
  9. 宣告名為counter的私有ParkingCounter屬性:

    	private final 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. 建立名為Sensor2的類,實現Runnable介面:

    public class Sensor2  implements Runnable {
    
  13. 宣告名為counter的私有ParkingCounter屬性:

    	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. 建立名為counter的ParkingCounter物件:

    		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");
    	}
    }
    

工作原理

ParkingCounter類繼承AtomicInteger類,包含兩個原子操作:carIn()和carOut()。本範例模擬控制進入停車場汽車數量的系統,停車場可以容納多輛車,用maxNumber屬性表示。

carIn()操作將停車場中實際的汽車數量和最大值不計較,如果相等,汽車無法進入停車場且此方法返回false值。否則,使用如下的原子操作結構:

  • 在區域性變數中獲取原子物件值。
  • 在不同的變數中儲存新值。
  • 使用compareAndSet()方法試圖將舊值替換成新值。如果此方法返回true,則作為引數傳送的舊值是變數值,因此方法改變值。因為carIn()方法返回true,所以此操作以原子方式進行。如果compareAndSet()方法返回false,則作為引數傳送的舊值不是變數值(其它執行緒修改了值),因此此操作不能以原子方式進行。操作再次開始,直到以原子方式完成。

carOut()方法類似於carIn()方法,還實現了兩個Runnable物件,分別使用carIn()和carOut()方法來模擬停車動作。當執行本範例時,能夠看到停車場永遠不會超過汽車數量的最大值。

更多關注

  • 第七章“併發集合”中的“使用原子變數”小節