基本執行緒同步(六)使用讀/寫鎖同步資料訪問
宣告:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González 譯者:許巧輝
使用讀/寫鎖同步資料訪問
鎖所提供的最重要的改進之一就是ReadWriteLock介面和唯一 一個實現它的ReentrantReadWriteLock類。這個類提供兩把鎖,一把用於讀操作和一把用於寫操作。同時可以有多個執行緒執行讀操作,但只有一個執行緒可以執行寫操作。當一個執行緒正在執行一個寫操作,不可能有任何執行緒執行讀操作。
在這個指南中,你將會學習如何使用ReadWriteLock介面實現一個程式,使用它來控制訪問一個儲存兩個產品價格的物件。
準備工作…
你應該事先閱讀使用Lock同步程式碼塊的指南,才能更好的理解這個食譜。
如何做…
按以下步驟來實現的這個例子:
1.建立PricesInfo類,用它來儲存兩個產品價格的資訊。
public class PricesInfo {
2.宣告兩個double型別的屬性,分別命名為price1和price2。
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()方法,用來建立這兩個屬性的值。它使用寫鎖來控制對它們的訪問。
public void setPrices(double price1, double price2) { lock.writeLock().lock(); this.price1=price1; this.price2=price2; lock.writeLock().unlock(); }
8.建立Reader類,並指定它實現Runnable介面。這個類實現了PricesInfo類屬性值的讀者。
public class Reader implements Runnable {
9.宣告一個PricesInfo物件,並且實現Reader類的構造器來初始化這個物件。
private PricesInfo pricesInfo; public Reader (PricesInfo pricesInfo){ this.pricesInfo=pricesInfo; }
10.實現Reader類的run()方法,它讀取10次兩個價格的值。
@Override public void run() { for (int i=0; i<10; i++){ System.out.printf("%s: Price 1: %f\n", Thread. currentThread().getName(),pricesInfo.getPrice1()); System.out.printf("%s: Price 2: %f\n", Thread. currentThread().getName(),pricesInfo.getPrice2()); } }
11.建立Writer類,並指定它實現Runnable介面。這個類實現了PricesInfo類屬性值的修改者。
public class Writer implements Runnable {
12.宣告一個PricesInfo物件,並且實現Writer類的構造器來初始化這個物件。
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("Writer: Attempt to modify the prices.\n"); pricesInfo.setPrices(Math.random()*10, Math.random()*8); System.out.printf("Writer: Prices have been modified.\n"); 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.建立5個Reader物件,並且用5個執行緒來執行它們。
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物件,並且用執行緒來執行它。
Writer writer=new Writer(pricesInfo); Thread threadWriter=new Thread(writer);
18.啟動這些執行緒。
for (int i=0; i<5; i++){ threadsReader[i].start(); } threadWriter.start();
它是如何工作的…
在以下截圖中,你可以看到執行這個例子的一個部分輸出:
正如我們前面提及到的,ReentrantReadWriteLock類有兩把鎖,一把用於讀操作,一把用於寫操作。用於讀操作的鎖,是通過在 ReadWriteLock介面中宣告的readLock()方法獲取的。這個鎖是實現Lock介面的一個物件,所以我們可以使用lock(), unlock() 和tryLock()方法。用於寫操作的鎖,是通過在ReadWriteLock介面中宣告的writeLock()方法獲取的。這個鎖是實現Lock接 口的一個物件,所以我們可以使用lock(), unlock() 和tryLock()方法。確保正確的使用這些鎖,使用它們與被設計的目的是一樣的,這是程式猿的職責。當你獲得Lock介面的讀鎖時,不能修改這個變數的值。否則,你可能會有資料不一致的錯誤。
參見
- 在第2章,基本執行緒同步中使用Lock同步程式碼塊的指南。
- 在第8章,測試併發應該程式中監控Lock介面的指南。