一、前言
在多線程開發中,我們更多關注的是多併發情況下,修改操作不會因爲併發而產生錯誤的情況,比如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方法執行的時候對讀進行了阻塞。