讀/寫鎖同步資料存取
讀/寫鎖同步資料存取
ReadWriteLock介面和實現此介面的ReentrantReadWriteLock特殊類是鎖機制提供的最顯著改進之一。此類包含兩種鎖:一種是讀取操作鎖,一種是寫入操作鎖。多個執行緒能夠同時使用讀操作鎖,但是隻有一個執行緒可以使用寫操作鎖。如果一個執行緒正在寫操作,其它執行緒均不可以讀寫。
在本節中,學習如何使用ReadWriteLock介面,實現程式用此介面控制儲存兩種產品價格的物件。
準備工作
本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。
###實現過程
通過如下步驟完成範例:
-
建立名為PricesInfo的類,用來儲存兩種產品的價格資訊:
public class PricesInfo {
-
定義兩個名為price1和price2的double型別屬性:
private double price1; private double price2;
-
定義名為lock的ReadWriteLock物件:
private ReadWriteLock lock;
-
實現類建構函式,初始化三個屬性。其中為lock屬性初始化新的ReentrantReadWriteLock物件:
public
-
實現getPrice1()方法,返回price1屬性值,使用讀鎖來控制使用此屬性值:
public double getPrice1() { lock.readLock().lock(); double value = price1; lock.readLock().unlock(); return value; }
-
實現getPrice2()方法,返回price2屬性值,使用讀鎖來控制使用此屬性值:
public double getPrice2() { lock.readLock().lock(); double value = price2; lock.readLock().unlock(); return value; }
-
實現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(); }
-
建立名為Reader的類,指定其實現Runnable介面。此類實現一個讀取程式,獲得PricesInfo類屬性的值:
public class Reader implements Runnable {
-
定義PricesInfo物件,實現初始化此物件的類建構函式:
private PricesInfo pricesInfo; public Reader (PricesInfo pricesInfo) { this.pricesInfo = pricesInfo; }
-
實現此類的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()); } }
-
建立名為Reader的類,指定其實現Runnable介面。此類實現一個修改程式,設定PricesInfo類屬性的值:
public class Writer implements Runnable{
-
定義PricesInfo物件,實現初始化此物件的類建構函式:
private PricesInfo pricesInfo; public Writer (PricesInfo pricesInfo) { this.pricesInfo = pricesInfo; }
-
實現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(); } } }
-
建立本範例中的主類,實現一個包含main()方法的Main類:
public class Main { public static void main(String[] args) {
-
建立PricesInfo物件:
PricesInfo pricesInfo = new PricesInfo();
-
建立五個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]); }
-
建立Writer物件和執行它的Thread物件:
Writer writer = new Writer(pricesInfo); Thread threadWriter = new Thread(writer);
-
啟動讀寫執行緒:
for (int i = 0 ; i < 5 ; i ++){ threadsReader[i].start(); } threadWriter.start();
工作原理
下圖顯示執行本範例輸出的部分內容:
當寫入程式控制寫鎖時,所有讀取程式都無法得到資料。通過控制檯可以看到Write Lock Acquired資訊後面有一些讀取程式資訊,這些時之前已經執行完並且沒有實時輸出的指令資訊。一旦寫入程式釋放了寫鎖,讀取程式將再次獲得訪問價格資訊的許可權,顯示新的價格。
如之前所述,ReentrantReadWriteLoc類包含兩種鎖:一種是讀取操作鎖,一種是寫入操作鎖。讀取操作使用的鎖通過ReadWriteLock介面定義的readLock()方法獲得。讀鎖是實現Lock介面的物件,所以可以使用lock()、unlock()、和tryLock()方法。寫入操作使用的鎖通過ReadWriteLock介面定義的writeLock()方法獲得。寫鎖也是實現Lock介面的物件,所以可以使用lock()、unlock()、和tryLock()方法。開發人員的職責是確保正確使用這些鎖,與其初始設計的使用目的相同。當使用Lock介面的讀鎖時,不能修改變數值。否則,可能出現與不一致性相關的資料錯誤。
更多關注
- 本章中”鎖同步程式碼塊”小節。
- 第九章“測試併發應用”中的“監控鎖介面”小節。