并发和多线程

2020-02-03 2.2.4 读写锁 ReadWriteLo

2020-02-03  本文已影响0人  FredWorks

本文是Java线程安全和并发编程知识总结的一部分。

2.2.4 读写锁

ReentrantLock实现的是一种标准的互斥锁,每次只允许一个线程获得锁操作。而现实中,大部分业务操作都是读操作多余写操作。只要确保多个线程可以读取到最新的数据,且期间不会有其他线程修改数据,那么实际上多个线程同时读,是不会引发业务上的冲突的。此时,并发性能会比RentrantLock更高。
实现这个意图的锁就是ReadWriteLock及其实现ReentrantReadWriteLock

  • 读写锁允许单独获取读锁和写锁。
  • 读锁允许多个线程同时申请并获得,从而实现多线程读。
  • 写锁只允许一个线程获得,从而确保只有一个线程在写。

需要说明的是:

  • ReadWriteLock 并未说明读锁和写锁是否是可重入锁。但ReentrantReadWriteLock是可重入锁实现。
  • ReadWriteLock 对锁获取插队问题没有规定。ReentrantReadWriteLock为公平锁时,不允许插队;为非公平锁时允许读锁插队到写锁而将写锁降级为读锁,但不允许写锁插队到读锁从而将读锁升级为写锁。

大体上而言,读写锁是否一定能提升性能?这个需要区分场景分析:

  • 如果一个任务中,读操作远远大于写操作,那么读写锁性能提升是明显的。
  • 如果一个任务中,读写操作的耗时大体相当,甚至写多余读,那么读写锁基本不会有什么性能提升。

需要注意的是:

  • 使用读写锁的代码是否会发生脏写,并不由读写锁代码本身控制,而是由使用读写锁的业务代码决定。假如一个线程在执行读操作时,有另外一个线程执行写操作了,那么读线程可能会读取到脏数据的。

下面是一个封装普通ArrayList使其读并发写串行的例子:

/**
 * @author xx
 * 2020年2月3日 下午6:02:32 xx添加此方法
 */
public class ReadWriteLockArrayList<T> extends ArrayList<T> {
    
    private static final long serialVersionUID = 5764776949955586441L;

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    private final Lock readLock = this.lock.readLock();
    
    private final Lock writeLock = this.lock.writeLock();
    
    private ArrayList<T> list;
    
    /**
     * 构造函数
     */
    public ReadWriteLockArrayList(int initialCapacity) {
        this.list = new ArrayList<T>(initialCapacity);
    }
    
    /**
     * 构造函数
     */
    public ReadWriteLockArrayList() {
        this.list = new ArrayList<T>();
    }
    
    /**
     * 构造函数
     */
    public ReadWriteLockArrayList(Collection<T> c) {
        this.list = new ArrayList<T>(c);
    }

    /**
     * 读操作,加读锁
     * 参看父类中的注释 @see java.util.ArrayList#get(int)
     * 2020年2月3日 下午6:02:32 xx添加此方法
     */
    public T get(int index) {
        this.readLock.lock();
        try {
            return this.list.get(index);
        } finally {
            this.readLock.unlock();
        }
    }

    /**
     * 写操作,加写锁
     * 参看父类中的注释 @see java.util.ArrayList#add(E e)
     * 2020年2月3日 下午6:02:32 xx添加此方法
     * @param e
     * @return
     */
    public boolean add(T e) {
        this.writeLock.lock();
        try {
            return this.list.add(e);
        } finally {
            this.writeLock.unlock();
        }
    }
    
    /**
     * 写操作,加写锁
     * 参看父类中的注释 @see java.util.ArrayList#trimToSize()
     * 2020年2月3日 下午6:02:32 xx添加此方法
     */
    public void trimToSize() {
        this.writeLock.lock();
        try {
            this.list.trimToSize();
        } finally {
            this.writeLock.unlock();
        }
    }
    
    /*
     * 其他操作类似上述行为
     */
}
上一篇下一篇

猜你喜欢

热点阅读