JDK7中ReentrantReadWriteLock源码解析(1)

来源:互联网 发布:淘宝联盟 微信遭投诉 编辑:程序博客网 时间:2024/06/15 00:57

ReentrantReadWriteLock中获取锁和释放锁的实现类似ReentrantLock,关于ReentrantLock中如何获取锁和释放锁可参考:

http://ericchunli.iteye.com/blog/2393341和http://ericchunli.iteye.com/blog/2393222

 

ReentrantReadWriteLock概述

ReentrantReadWriteLock支持与ReentrantLock类似语义的ReadWriteLock实现,且具有以下属性:

(a)获取顺序

ReentrantReadWriteLock不会将读取者优先或写入者优先强加给锁访问的排序,但是它确实支持可选的公平策略。

(b)非公平模式(默认)

当非公平地(默认)构造时,未指定进入读写锁的顺序,受到reentrancy约束的限制。连续竞争的非公平锁可能无限期地推迟一个或多个reader或writer线程,但吞吐量通常要高于公平锁。

(c)公平模式

当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入。当释放当前保持的锁时,可以为等待时间最长的单个writer线程分配写入锁,如果有一组等待时间大于所有正在等待的writer线程的reader线程,将为该组分配写入锁。如果保持写入锁或者有一个等待的writer线程,则试图获得公平读取锁(非重入地)的线程将会阻塞。直到当前最旧的等待writer线程已获得并释放了写入锁之后,该线程才会获得读取锁。当然如果等待writer放弃其等待,而保留一个或更多reader线程为队列中带有写入锁自由的时间最长的waiter,则将为那些reader分配读取锁。试图获得公平写入锁的(非重入地)的线程将会阻塞,除非读取锁和写入锁都自由(这意味着没有等待线程)。(注意:非阻ReentrantReadWriteLock.ReadLock.tryLock()和ReentrantReadWriteLock.WriteLock.tryLock()方法不会遵守此公平设置,并将获得锁(如果可能),不考虑等待线程)。

(d)重入

ReentrantReadWriteLock允许reader和writer按照ReentrantLock的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入reader使用它们。此外writer可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果reader试图获取写入锁,那么将永远不会获得成功。

可重入的读写锁的特点:

  (d.1)当有线程获取读锁时,不允许再有线程获得写锁

  (d.2)当有线程获得写锁时,不允许其他线程获得读锁和写锁

(e)锁降级

重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是从读取锁升级到写入锁是不可能的。

  (e.1)可以同时有多个线程同时获得读锁,进入临界区。这时候的读锁的行为和Semaphore信号量是类似的

  (e.2)由于是可重入的,所以1个线程如果获得了读锁,那么它可以重入这个读锁

  (e.3)如果1个线程获得了读锁,那么它不能同时再获得写锁,这个就是所谓的“锁升级”,读锁升级到写锁可能会造成死锁,所以是不允许的

  (e.4)如果1个线程获得了写锁,那么不允许其他线程再获得读锁和写锁,但是它自己可以获得读锁,就是所谓的“锁降级”,锁降级是允许的

(f)锁获取的中断

读取锁和写入锁都支持锁获取期间的中断。

(f)Condition支持

写入锁提供了一个Condition实现,对于写入锁来说,该实现的行为与ReentrantLock.newCondition()提供的Condition实现对ReentrantLock所做的行为相同。当然此Condition只能用于写入锁。读取锁不支持Condition,readLock().newCondition()会抛出 UnsupportedOperationException。

(g)监测

ReentrantReadWriteLock支持一些确定是保持锁还是争用锁的方法。这些方法设计用于监视系统状态,而不是同步控制。ReentrantReadWriteLock行为的序列化方式与内置锁的相同:反序列化的锁处于解除锁状态,无论序列化该锁时其状态如何。

ReentrantReadWriteLock的示例用法

利用重入来执行升级缓存后的锁降级(省略了异常处理):

public class CachedData {

  Object data;

  volatile boolean cacheValid;

  ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

 

  public void processCachedData() {

    rwl.readLock().lock();

    if (!cacheValid) {

      // Must release read lock before acquiring write lock

      rwl.readLock().unlock();

      rwl.writeLock().lock();

      // Recheck state因为其它线程可能获得写锁并更改状态before we did.

      if (!cacheValid) {

        data = ...

        cacheValid = true;

      }

      // Downgrade by acquiring read lock before releasing write lock

      rwl.readLock().lock();

      rwl.writeLock().unlock(); // Unlock write, still hold read

    }

    use(data);

    rwl.readLock().unlock();

  }

}

 

在使用某些种类的Collection时,可以使用ReentrantReadWriteLock来提高并发性。通常在预期collection很大,读取者线程访问它的次数多于写入者线程,并且entail操作的开销高于同步开销时。

public class RWDictionary {

  private final Map<String, Data> m = new TreeMap<String, Data>();

  private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

  private final Lock r = rwl.readLock();

  private final Lock w = rwl.writeLock();

 

  public Data get(String key) {

    r.lock();

    try { return m.get(key); }

    finally { r.unlock(); }

  }

  public String[] allKeys() {

    r.lock();

    try { return m.keySet().toArray(); }

    finally { r.unlock(); }

  }

  public Data put(String key, Data value) {

    w.lock();

    try { return m.put(key, value); }

    finally { w.unlock(); }

  }

  public void clear() {

    w.lock();

    try { m.clear(); }

    finally { w.unlock(); }

  }

}

实现注意事项:

ReentrantReadWriteLock最多支持65535个递归写入锁和65535个读取锁,试图超出这些限制将导致锁方法抛出Error。

 

public class ReentrantReadWriteLock implements ReadWriteLock, Serializable;

相关重要属性:

private final ReentrantReadWriteLock.ReadLock readerLock; // 提供读锁的内部类

private final ReentrantReadWriteLock.WriteLock writerLock; // 提供写锁的内部类

final Sync sync;

// 类似ReentrantLock的Sync,抽象类且继承AQS,用来实现所有同步机制

 

// 默认的构造函数,使用非公平锁

public ReentrantReadWriteLock() { this(false); }

// 创建给定公平策略的ReentrantReadWriteLock,true表示锁应当使用公平排序策略

public ReentrantReadWriteLock(boolean fair) {

  sync = fair ? new FairSync() : new NonfairSync();

  readerLock = new ReadLock(this);

  writerLock = new WriteLock(this);

}

 

Sync的实现: 

abstract static class Sync extends AbstractQueuedSynchronizer{

  abstract boolean readerShouldBlock();

  abstract boolean writerShouldBlock();

  // 如果当前线程试图获得读/写锁,并且能够处理则返回true,否则应该阻塞

}

 

static final class NonfairSync extends Sync {

  // 写锁总是优先获取,不考虑AQS队列中先来的线程

  final boolean writerShouldBlock() { return false;}

  // 读锁也不按FIFO队列排队,而是看当前获得锁是否是写锁,如果是写锁就等待,否则就尝试获得锁

  final boolean readerShouldBlock() {

    return apparentlyFirstQueuedIsExclusive();

  }

}

// CLH队列中首节点不为空,后继节点不为空且是独占式的,后继节点的thread不为null

final boolean apparentlyFirstQueuedIsExclusive() {

  Node h, s;

  return (h = head) != null && (s = h.next) != null && !s.isShared() && s.thread != null;

}

 

static final class FairSync extends Sync {

  // 如果存在其它锁,获取锁失败就得进AQS队列等待

  final boolean writerShouldBlock() { return hasQueuedPredecessors(); }

  final boolean readerShouldBlock() { return hasQueuedPredecessors(); }

}

// AQS:判断是否存在等待更久的线程,同ReentrantLock实现一致

public final boolean hasQueuedPredecessors() {

  Node t = tail; 

  Node h = head;

  Node s;

  return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());

}

 

读锁的获取与释放:

public static class ReadLock implements Lock, java.io.Serializable {

  private final Sync sync;

  protected ReadLock(ReentrantReadWriteLock lock) {

    sync = lock.sync;

  }

  public void lock() {

    sync.acquireShared(1);

  }

  public void unlock() {

    sync.releaseShared(1);

  }

}

 

写锁的获取与释放:

public static class WriteLock implements Lock, java.io.Serializable {

  private final Sync sync;

  protected WriteLock(ReentrantReadWriteLock lock) {

    sync = lock.sync;

  }

  public void unlock() {

    sync.release(1);

  }

  public void lock() {

    sync.acquire(1);

  }

}

 

获取读写锁:

public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }

public ReentrantReadWriteLock.ReadLock readLock()  { return readerLock; }

 

实现总结:ReentrantReadWriteLock的内部维护了Sync的对象引用,Sync是读写锁ReentrantReadWriteLock的抽象静态内部类,它继承自AbstractQueuedSynchronizer【AQS】。Sync根据ReentrantReadWriteLock提供的公平锁和非公平锁机制,Sync也提供其对应的实现:FairSync和NonfairSync。ReentrantReadWriteLock根据读写的需要,分别提供了读锁和写锁:WriteLock和ReadLock。Sync提供了所有实现机制的同步器,这里可以看出虽然对写锁ReentrantReadWriteLock分别提供了读锁和写锁【WriteLock和ReadLock】来实现锁的释放和获取,但是究其根源可得知锁的获取与释放都是由Sync来实现的。

 

对于非公平锁,写锁通常是优先获取且不会阻塞【这里的阻塞应该是指不管存不存在等待更久的读锁都优先获取写锁】,但是对于读锁则需要做如下判断来确定是否需要阻塞【true表示需要】:(h=head)!=null&&(s=h.next)!=null&&!s.isShared()&&s.thread!=null;对于公平锁,无论是写锁还是读锁的获取都需要判断CLH队列中是否存在等待更久的锁,判断的标准如下:h!=t&&((s=h.next)==null||s.thread!=Thread.currentThread());

 

这里只简单的介绍ReentrantReadWriteLock是如何获取锁以及释放锁,更多关于如何利用CLH来实现读写锁的获取和释放,可参考:http://ericchunli.iteye.com/blog/2395933和http://ericchunli.iteye.com/blog/2396018的实现解析。