1. 程式人生 > >讀/寫鎖同步資料存取

讀/寫鎖同步資料存取

讀/寫鎖同步資料存取


ReadWriteLock介面和實現此介面的ReentrantReadWriteLock特殊類是鎖機制提供的最顯著改進之一。此類包含兩種鎖:一種是讀取操作鎖,一種是寫入操作鎖。多個執行緒能夠同時使用讀操作鎖,但是隻有一個執行緒可以使用寫操作鎖。如果一個執行緒正在寫操作,其它執行緒均不可以讀寫。

在本節中,學習如何使用ReadWriteLock介面,實現程式用此介面控制儲存兩種產品價格的物件。

準備工作

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

###實現過程

通過如下步驟完成範例:

  1. 建立名為PricesInfo的類,用來儲存兩種產品的價格資訊:

    public class PricesInfo {
    
  2. 定義兩個名為price1和price2的double型別屬性:

    	private double price1;
    	private double price2;
    
  3. 定義名為lock的ReadWriteLock物件:

    	private ReadWriteLock lock;
    
  4. 實現類建構函式,初始化三個屬性。其中為lock屬性初始化新的ReentrantReadWriteLock物件:

    	public
    PricesInfo(){ price1 = 1.0; price2 = 2.0; lock = new ReentrantReadWriteLock(); }
  5. 實現getPrice1()方法,返回price1屬性值,使用讀鎖來控制使用此屬性值:

    	public double getPrice1() {
    		lock.readLock().lock();
    		double value = price1;
    		lock.readLock().unlock();
    		return value;
    	}
    
  6. 實現getPrice2()方法,返回price2屬性值,使用讀鎖來控制使用此屬性值:

    	public double getPrice2() {
    		lock.readLock().lock();
    		double value = price2;
    		lock.readLock().unlock();
    		return value;
    	}
    
  7. 實現setPrices()方法來確定兩個使用寫鎖來控制使用的屬性值。設定執行緒休眠10秒鐘,這表明儘管它在使用寫鎖,但也沒有其它執行緒控制讀鎖:

    	public void setPrices(double price1, double price2){
    		lock.writeLock().lock();
    		System.out.printf("%s: PricesInfo: Write Lock Adquired.\n", new Date());
    		try {
    			TimeUnit.SECONDS.sleep(10);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		this.price1 = price1;
    		this.price2 = price2;
    		System.out.printf("%s: PricesInfo: Write Lock Released.\n", new Date());
    		lock.writeLock().unlock();
    	}
    
  8. 建立名為Reader的類,指定其實現Runnable介面。此類實現一個讀取程式,獲得PricesInfo類屬性的值:

    public class Reader implements Runnable {
    
  9. 定義PricesInfo物件,實現初始化此物件的類建構函式:

    	private PricesInfo pricesInfo;
    	
    	public Reader (PricesInfo pricesInfo) {
    		this.pricesInfo = pricesInfo;
    	}
    
  10. 實現此類的run()方法,讀取10遍兩種商品的價格:

    	@Override
    	public void run() {
    		for( int i = 0 ; i < 10 ; i++){
    			System.out.printf("%s : %s : Price 1 : %f\n", new Date(), Thread.currentThread().getName(), pricesInfo.getPrice1());
    			System.out.printf("%s : %s : Price 2 : %f\n", new Date(), Thread.currentThread().getName(), pricesInfo.getPrice2());
    		}
    	}
    
  11. 建立名為Reader的類,指定其實現Runnable介面。此類實現一個修改程式,設定PricesInfo類屬性的值:

    public class Writer implements Runnable{
    
  12. 定義PricesInfo物件,實現初始化此物件的類建構函式:

    	private PricesInfo pricesInfo;
    	
    	public Writer (PricesInfo pricesInfo) {
    		this.pricesInfo = pricesInfo;
    	}
    
  13. 實現run()方法,迴圈修改兩種商品價格三次,每次之間休眠2秒:

    	@Override
    	public void run() {
    		for(int i = 0 ; i < 3 ; i++){
    			System.out.printf("%s : Writer : Attempt to modify the prices.\n", new Date());
    			pricesInfo.setPrices(Math.random() * 10,  Math.random() * 8);
    			System.out.printf("%s : Writer : Prices have been modified.\n", new Date());
    			try {
    				Thread.sleep(2);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
  14. 建立本範例中的主類,實現一個包含main()方法的Main類:

    public class Main {
    	public static void main(String[] args) {
    
  15. 建立PricesInfo物件:

    		PricesInfo pricesInfo = new PricesInfo();
    
  16. 建立五個Reader物件和五個執行它們的Thread物件:

    		Reader readers[] = new Reader[5];
    		Thread threadsReader[] = new Thread[5];
    		for (int i = 0 ; i < 5 ; i ++){
    			readers[i] = new Reader(pricesInfo);
    			threadsReader[i] = new Thread(readers[i]);
    		}
    
  17. 建立Writer物件和執行它的Thread物件:

    		Writer writer = new Writer(pricesInfo);
    		Thread threadWriter = new Thread(writer);
    
  18. 啟動讀寫執行緒:

    		for (int i = 0 ; i < 5 ; i ++){
    			threadsReader[i].start();
    		}
    		threadWriter.start();
    

工作原理

下圖顯示執行本範例輸出的部分內容:

pics/02_05.jpg

當寫入程式控制寫鎖時,所有讀取程式都無法得到資料。通過控制檯可以看到Write Lock Acquired資訊後面有一些讀取程式資訊,這些時之前已經執行完並且沒有實時輸出的指令資訊。一旦寫入程式釋放了寫鎖,讀取程式將再次獲得訪問價格資訊的許可權,顯示新的價格。

如之前所述,ReentrantReadWriteLoc類包含兩種鎖:一種是讀取操作鎖,一種是寫入操作鎖。讀取操作使用的鎖通過ReadWriteLock介面定義的readLock()方法獲得。讀鎖是實現Lock介面的物件,所以可以使用lock()、unlock()、和tryLock()方法。寫入操作使用的鎖通過ReadWriteLock介面定義的writeLock()方法獲得。寫鎖也是實現Lock介面的物件,所以可以使用lock()、unlock()、和tryLock()方法。開發人員的職責是確保正確使用這些鎖,與其初始設計的使用目的相同。當使用Lock介面的讀鎖時,不能修改變數值。否則,可能出現與不一致性相關的資料錯誤。

更多關注

  • 本章中”鎖同步程式碼塊”小節。
  • 第九章“測試併發應用”中的“監控鎖介面”小節。