一、前言
在多线程开发中,我们更多关注的是多并发情况下,修改操作不会因为并发而产生错误的情况,比如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方法执行的时候对读进行了阻塞。