ReentrantReadWriteLock源码分析

概述

ReadWriteLock

ReadWriteLock持有一对锁,一个是用于读操作的readLock,一个用于写操作的writeLock。readLock是共享锁,可以被多个读线程同时持有,writeLock是独占锁。

ReadWriteLock必须保证writeLock操作的内存一致性。也就是说,持有readLock的线程能看到先前writeLock释放后的所有更新。

与只使用一个独占锁相比,ReadWriteLock能对访问共享数据支持更高的并发。ReadWriteLock实现的并发场景如下:修改数据时只有一个写线程在修改,读取数据时可以有多个读线程并发读取。

ReadWriteLock是否能比独占锁拥有更高的性能,取决于数据读取的频率是否比数据修改的频率更高、读操作和写操作的持续时间、竞争读/写数据的激烈程度 —— 即在同一时间尝试读/写数据的线程数量。

ReentrantReadWriteLock

ReentrantReadWriteLock是ReadWriteLock接口的实现类,支持与ReentrantLock相似的语义。它支持以下属性:

1、非公平模式

当设置为非公平模式时,获得readLock和writeLock的顺序是不确定的。非公平锁可能会导致在竞争一直发生时,某个读线程或者某个写线程会无限期等待,但是他通常比公平锁拥有更高的吞吐量。

2、公平模式

当设置为公平模式时,对竞争锁的线程使用的是先到先得的策略。当锁释放时,等待时间最长的写线程会获得writeLock,或者如果一批读线程的等待时间比所有写线程的等待时间更长,则该读线程组会获得readLock。

尝试获得fair write lock的线程会一直阻塞,直到readLock和writeLock都已经释放(即当前无等待线程)。注意调用非阻塞方法ReadLock#tryLock()方法、writeLock#tryLock()方法不遵守这一fair约定。它们都会尝试立即获得锁,不管当前有没有等待线程。

3、可重入性

ReadLock和WriteLock都是可重入的,此外,写线程也可以获得readLock,但读线程不可以获得writeLock。这是有用的,比如在获得writeLock期间调用需要获得readLock的方法时。如果一个读线程尝试获得writeLock,实时永远不可能成功的。

4、锁降级

可重入性允许一个writeLock降级为readLock,降级方法是:获得writeLock,然后是readLock,然后释放readLock。但是将一个readLock升级为writeLock是不可能的。

5、获得锁期间是否支持中断

readLock和writeLock在获得锁期间都支持中断。

6、Condition支持

writeLock支持Condition,可以通过ReentrantLock#newCondition()方法创建Condition。但是readLock不支持Condition,调用readLock().newCondition()方法会抛出操作不支持异常。

使用示例

class CachedData {
   Object data;
   volatile boolean cacheValid;
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
     }

     try {
       use(data);
     } finally {
       rwl.readLock().unlock();
     }
   }
 }

成员变量

 ReentrantReadWriteLock定义了2个内部类:ReadLock和WriteLock,将他们的实例作为核心的成员变量。

/** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;

ReadLock

lock()方法

ReadLock在调用lock()方法获取读锁时,会首先判断当前是否有其他线程占有了写锁。

  • 如果没有其他线程占有了写锁,lock()方法立即返回。
  • 如果有其他线程占有了写锁,该线程陷入阻塞,直至获得读锁;

lockInterruptibly()方法  

ReadLock在调用lockInterruptibly()方法获取读锁时,会首先判断当前是否有其他线程占有了写锁。

  • 如果没有其他线程占有了写锁,lock()方法立即返回。
  • 如果有其他线程占有了写锁,该线程陷入阻塞,只有发生以下情形才能解除阻塞:1、获取到了读锁;2、其他线程对该线程进行了中断,随后该线程会抛出中断异常。

 tryLock()方法

  • 如果没有其他线程占有了写锁,立即获得读锁,并返回true。该方法打破了公平策略,只要读锁可用,则立即获得读锁,即使当前有线程正在等待获得读锁。
  • 如果有其他线程占有了写锁,返回false。
public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        public void lock() {
            sync.acquireShared(1);
        }

        public void lockInterruptibly() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }

        public boolean tryLock() {
            return sync.tryReadLock();
        }

        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
        }

        public void unlock() {
            sync.releaseShared(1);
        }

        public Condition newCondition() {
            throw new UnsupportedOperationException();
        }

 
    }

 

你可能感兴趣的:(java高并发实践)