1. 程式人生 > >基本執行緒同步(六)使用讀/寫鎖同步資料訪問

基本執行緒同步(六)使用讀/寫鎖同步資料訪問

宣告:本文是《 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();

它是如何工作的…

在以下截圖中,你可以看到執行這個例子的一個部分輸出:

5

正如我們前面提及到的,ReentrantReadWriteLock類有兩把鎖,一把用於讀操作,一把用於寫操作。用於讀操作的鎖,是通過在 ReadWriteLock介面中宣告的readLock()方法獲取的。這個鎖是實現Lock介面的一個物件,所以我們可以使用lock(), unlock() 和tryLock()方法。用於寫操作的鎖,是通過在ReadWriteLock介面中宣告的writeLock()方法獲取的。這個鎖是實現Lock接 口的一個物件,所以我們可以使用lock(), unlock() 和tryLock()方法。確保正確的使用這些鎖,使用它們與被設計的目的是一樣的,這是程式猿的職責。當你獲得Lock介面的讀鎖時,不能修改這個變數的值。否則,你可能會有資料不一致的錯誤。

參見

  • 在第2章,基本執行緒同步中使用Lock同步程式碼塊的指南。
  • 在第8章,測試併發應該程式中監控Lock介面的指南。