Java讀寫鎖ReadWriteLock

一、前言

在多線程開發中,我們更多關注的是多併發情況下,修改操作不會因爲併發而產生錯誤的情況,比如1000個線程對一個參數同時加1,最終得到的是小於1000的數字。這樣的情況下我們有很多方法來保證其線程安全,比如使用synchronized關鍵字或者使用ava.util.concurrent包內部的類或者方法。
但其實還有一種情況,同時存在讀寫併發的情形,這個時候我們希望讀寫分離,就是對於讀取這個動作來說,可以同時有多個線程同時去讀取這個資源,但是對於寫這個動作來說,只能同時有一個線程來操作,而且同時,當有一個寫線程在操作這個資源的時候,其他的讀線程是不能來操作這個資源的,這樣就極大的發揮了多線程的特點,能很好的將多線程的能力發揮出來。
針對這個需求,java提供了ReadWriteLock這個接口就爲我們實現了,他的實現類ReentrantReadWriteLock我們可以很簡單的來實現剛纔的效果。

二、ReadWriteLock

我們先通過一個例子看一下,先建一個產品價格類

public class ProductPrice {
    //現在產品價格
    private double nowprice;
    //鎖
    private ReadWriteLock lock = new ReentrantReadWriteLock();;
    //修改價格
    public void setPrice(double price){
        lock.writeLock().lock();
        this.nowprice = price;
        lock.writeLock().unlock();
    }
    //獲取當前價格
    public double getPrice(){
        lock.readLock().lock();
        double value = this.nowprice;
        lock.readLock().unlock();
        return value;
    }
    //將價格自增1000
    public void add(){
        lock.writeLock().lock();
        for(int i=0;i<1000;i++){
            this.nowprice = this.nowprice + 1;
        }
        lock.writeLock().unlock();
    }
}

這其中set和add使用了write鎖,get使用了read鎖
然後再建分辨建一個Reader類和Writer類來模擬併發讀寫操作

public class Reader implements Runnable {

    private ProductPrice productPrice;

    public Reader(ProductPrice productPrice) {
        this.productPrice = productPrice;
    }
    @Override
    public void run(){
        System.out.printf("%s: 此時產品價格: %f\n", Thread.currentThread().getName(), productPrice.getPrice());
    }

}

Writer類使用了10ms的延遲,保證在寫操作在讀操作之間進行。

public class Writer implements Runnable {

    private ProductPrice productPrice;

    public Writer(ProductPrice productPrice) {
        this.productPrice = productPrice;
    }

    @Override
    public void run() {
        try{
            Thread.sleep(10);
        }catch(Exception e){
            e.printStackTrace();
        }
        double price = Math.random() * 10;
        productPrice.setPrice(price);
        System.out.println("Writer: Prices have been modified:"+price);
    }
}

最後使用一個Test類檢測一下

public class TestSingleObject {
    public static void main(String[] args) {
        ProductPrice productPrice = new ProductPrice();

        Writer writer = new Writer(productPrice);
        Thread threadWriter = new Thread(writer);
        threadWriter.start();

        Reader readers[] = new Reader[100];
        Thread threadsReader[] = new Thread[100];
        for (int i = 0; i < 100; i++){
            readers[i] = new Reader(productPrice);
            threadsReader[i] = new Thread(readers[i]);
            threadsReader[i].start();
        }
        System.out.println("----------------------"+productPrice.getPrice());
    }
}

執行結果如下:

Thread-1: 此時產品價格: 0.000000
Thread-100: 此時產品價格: 3.702643
----------------------3.7026426089474764
Thread-99: 此時產品價格: 3.702643
Thread-98: 此時產品價格: 3.702643
Thread-97: 此時產品價格: 3.702643
Thread-96: 此時產品價格: 3.702643
Thread-95: 此時產品價格: 3.702643
Thread-94: 此時產品價格: 3.702643
Thread-93: 此時產品價格: 3.702643
Thread-92: 此時產品價格: 3.702643
Thread-91: 此時產品價格: 3.702643
Thread-90: 此時產品價格: 3.702643
Thread-89: 此時產品價格: 3.702643
Writer: Prices have been modified:3.7026426089474764
Thread-88: 此時產品價格: 3.702643
Thread-87: 此時產品價格: 3.702643
Thread-86: 此時產品價格: 3.702643
Thread-85: 此時產品價格: 3.702643
Thread-84: 此時產品價格: 3.702643
Thread-83: 此時產品價格: 3.702643
Thread-82: 此時產品價格: 3.702643
Thread-81: 此時產品價格: 3.702643
Thread-80: 此時產品價格: 3.702643
Thread-79: 此時產品價格: 3.702643
Thread-78: 此時產品價格: 3.702643
Thread-77: 此時產品價格: 3.702643
Thread-76: 此時產品價格: 3.702643
Thread-75: 此時產品價格: 3.702643
Thread-74: 此時產品價格: 3.702643
Thread-73: 此時產品價格: 3.702643
Thread-72: 此時產品價格: 0.000000
Thread-71: 此時產品價格: 0.000000
Thread-70: 此時產品價格: 0.000000
Thread-69: 此時產品價格: 0.000000
Thread-68: 此時產品價格: 0.000000
Thread-67: 此時產品價格: 0.000000
Thread-66: 此時產品價格: 0.000000
Thread-65: 此時產品價格: 0.000000
Thread-64: 此時產品價格: 0.000000
Thread-63: 此時產品價格: 0.000000
Thread-62: 此時產品價格: 0.000000
Thread-61: 此時產品價格: 0.000000
Thread-60: 此時產品價格: 0.000000
Thread-59: 此時產品價格: 0.000000
Thread-58: 此時產品價格: 0.000000
Thread-57: 此時產品價格: 0.000000
Thread-56: 此時產品價格: 0.000000
Thread-55: 此時產品價格: 0.000000
Thread-54: 此時產品價格: 0.000000
Thread-53: 此時產品價格: 0.000000
Thread-52: 此時產品價格: 0.000000
Thread-51: 此時產品價格: 0.000000
Thread-50: 此時產品價格: 0.000000
Thread-49: 此時產品價格: 0.000000
Thread-48: 此時產品價格: 0.000000
Thread-47: 此時產品價格: 0.000000
Thread-46: 此時產品價格: 0.000000
Thread-45: 此時產品價格: 0.000000
Thread-44: 此時產品價格: 0.000000
Thread-43: 此時產品價格: 0.000000
Thread-42: 此時產品價格: 0.000000
Thread-41: 此時產品價格: 0.000000
Thread-40: 此時產品價格: 0.000000
Thread-39: 此時產品價格: 0.000000
Thread-38: 此時產品價格: 0.000000
Thread-37: 此時產品價格: 0.000000
Thread-36: 此時產品價格: 0.000000
Thread-35: 此時產品價格: 0.000000
Thread-34: 此時產品價格: 0.000000
Thread-33: 此時產品價格: 0.000000
Thread-32: 此時產品價格: 0.000000
Thread-31: 此時產品價格: 0.000000
Thread-30: 此時產品價格: 0.000000
Thread-29: 此時產品價格: 0.000000
Thread-28: 此時產品價格: 0.000000
Thread-27: 此時產品價格: 0.000000
Thread-26: 此時產品價格: 0.000000
Thread-25: 此時產品價格: 0.000000
Thread-24: 此時產品價格: 0.000000
Thread-23: 此時產品價格: 0.000000
Thread-22: 此時產品價格: 0.000000
Thread-21: 此時產品價格: 0.000000
Thread-20: 此時產品價格: 0.000000
Thread-19: 此時產品價格: 0.000000
Thread-18: 此時產品價格: 0.000000
Thread-17: 此時產品價格: 0.000000
Thread-16: 此時產品價格: 0.000000
Thread-15: 此時產品價格: 0.000000
Thread-14: 此時產品價格: 0.000000
Thread-13: 此時產品價格: 0.000000
Thread-12: 此時產品價格: 0.000000
Thread-11: 此時產品價格: 0.000000
Thread-10: 此時產品價格: 0.000000
Thread-9: 此時產品價格: 0.000000
Thread-8: 此時產品價格: 0.000000
Thread-7: 此時產品價格: 0.000000
Thread-6: 此時產品價格: 0.000000
Thread-5: 此時產品價格: 0.000000
Thread-4: 此時產品價格: 0.000000
Thread-3: 此時產品價格: 0.000000
Thread-2: 此時產品價格: 0.000000

Process finished with exit code 0

我們可以發現100個線程中,自第73個開始價格被變更了,讀寫操作是互斥執行的,我們再使用add自增的方法來驗證一下。

public class Writer implements Runnable {

    private ProductPrice productPrice;

    public Writer(ProductPrice productPrice) {
        this.productPrice = productPrice;
    }

    @Override
    public void run() {
        try{
            Thread.sleep(10);
        }catch(Exception e){
            e.printStackTrace();
        }
        //productPrice.setPrice(price);
        productPrice.add();
    }
}

將Writer類中的setPrice方法改爲執行add自增1000的方法,然後再使用測試類得到結果

Thread-1: 此時產品價格: 0.000000
----------------------1000.0
Thread-100: 此時產品價格: 1000.000000
Thread-99: 此時產品價格: 1000.000000
Thread-98: 此時產品價格: 1000.000000
Thread-97: 此時產品價格: 1000.000000
Thread-96: 此時產品價格: 1000.000000
Thread-95: 此時產品價格: 1000.000000
Thread-94: 此時產品價格: 1000.000000
Thread-93: 此時產品價格: 1000.000000
Thread-92: 此時產品價格: 1000.000000
Thread-91: 此時產品價格: 1000.000000
Thread-90: 此時產品價格: 1000.000000
Thread-89: 此時產品價格: 1000.000000
Thread-88: 此時產品價格: 1000.000000
Thread-87: 此時產品價格: 1000.000000
Thread-86: 此時產品價格: 1000.000000
Thread-85: 此時產品價格: 1000.000000
Thread-84: 此時產品價格: 0.000000
Thread-83: 此時產品價格: 0.000000
Thread-82: 此時產品價格: 0.000000
Thread-81: 此時產品價格: 0.000000
Thread-80: 此時產品價格: 0.000000
Thread-79: 此時產品價格: 0.000000
Thread-78: 此時產品價格: 0.000000
Thread-77: 此時產品價格: 0.000000
Thread-76: 此時產品價格: 0.000000
Thread-75: 此時產品價格: 0.000000
Thread-74: 此時產品價格: 0.000000
Thread-73: 此時產品價格: 0.000000
Thread-72: 此時產品價格: 0.000000
Thread-71: 此時產品價格: 0.000000
Thread-70: 此時產品價格: 0.000000
Thread-69: 此時產品價格: 0.000000
Thread-68: 此時產品價格: 0.000000
Thread-67: 此時產品價格: 0.000000
Thread-66: 此時產品價格: 0.000000
Thread-65: 此時產品價格: 0.000000
Thread-64: 此時產品價格: 0.000000
Thread-63: 此時產品價格: 0.000000
Thread-62: 此時產品價格: 0.000000
Thread-61: 此時產品價格: 0.000000
Thread-60: 此時產品價格: 0.000000
Thread-59: 此時產品價格: 0.000000
Thread-58: 此時產品價格: 0.000000
Thread-57: 此時產品價格: 0.000000
Thread-56: 此時產品價格: 0.000000
Thread-55: 此時產品價格: 0.000000
Thread-54: 此時產品價格: 0.000000
Thread-53: 此時產品價格: 0.000000
Thread-52: 此時產品價格: 0.000000
Thread-51: 此時產品價格: 0.000000
Thread-50: 此時產品價格: 0.000000
Thread-49: 此時產品價格: 0.000000
Thread-48: 此時產品價格: 0.000000
Thread-47: 此時產品價格: 0.000000
Thread-46: 此時產品價格: 0.000000
Thread-45: 此時產品價格: 0.000000
Thread-44: 此時產品價格: 0.000000
Thread-43: 此時產品價格: 0.000000
Thread-42: 此時產品價格: 0.000000
Thread-41: 此時產品價格: 0.000000
Thread-40: 此時產品價格: 0.000000
Thread-39: 此時產品價格: 0.000000
Thread-38: 此時產品價格: 0.000000
Thread-37: 此時產品價格: 0.000000
Thread-36: 此時產品價格: 0.000000
Thread-35: 此時產品價格: 0.000000
Thread-34: 此時產品價格: 0.000000
Thread-33: 此時產品價格: 0.000000
Thread-32: 此時產品價格: 0.000000
Thread-31: 此時產品價格: 0.000000
Thread-30: 此時產品價格: 0.000000
Thread-29: 此時產品價格: 0.000000
Thread-28: 此時產品價格: 0.000000
Thread-27: 此時產品價格: 0.000000
Thread-26: 此時產品價格: 0.000000
Thread-25: 此時產品價格: 0.000000
Thread-24: 此時產品價格: 0.000000
Thread-23: 此時產品價格: 0.000000
Thread-22: 此時產品價格: 0.000000
Thread-21: 此時產品價格: 0.000000
Thread-20: 此時產品價格: 0.000000
Thread-19: 此時產品價格: 0.000000
Thread-18: 此時產品價格: 0.000000
Thread-17: 此時產品價格: 0.000000
Thread-16: 此時產品價格: 0.000000
Thread-15: 此時產品價格: 0.000000
Thread-14: 此時產品價格: 0.000000
Thread-13: 此時產品價格: 0.000000
Thread-12: 此時產品價格: 0.000000
Thread-11: 此時產品價格: 0.000000
Thread-10: 此時產品價格: 0.000000
Thread-9: 此時產品價格: 0.000000
Thread-8: 此時產品價格: 0.000000
Thread-7: 此時產品價格: 0.000000
Thread-6: 此時產品價格: 0.000000
Thread-5: 此時產品價格: 0.000000
Thread-4: 此時產品價格: 0.000000
Thread-2: 此時產品價格: 0.000000
Thread-3: 此時產品價格: 0.000000

可以看出從第85個線程Price價格直接更改到了1000,沒有出現0-1000之間的數字,這說明鎖的機制保證了add方法執行的時候對讀進行了阻塞。

發佈了75 篇原創文章 · 獲贊 90 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章