ReadWriteLock是ReentrantReadWriteLock的接口,而ReentrantReadWriteLock实现类中包括子类ReadLock和WriteLock。
首先来看一下ReadWriteLock接口中方法的定义:
public interface ReadWriteLock { Lock readLock(); // 返回用于读取操作的锁 Lock writeLock(); // 返回用于写入操作的锁 }读取锁和写入锁不可以同时存储,且读取锁可以同时存在多个,但是写入锁只能存在一个。
private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁 private final ReentrantReadWriteLock.WriteLock writerLock;// 写锁 public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } // 默认为非公平锁 public ReentrantReadWriteLock() { this(false); } public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(this); writerLock = new WriteLock(this); }
定义了读锁和写锁变量,同时提供了两个构造函数,用来构造公平或非公平锁。
1、获取共享读锁
ReadLock内部类的实现源代码如下:
// 获取读锁,是共享锁 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); } //Acquires the read lock unless the current thread is interrupted. 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(); } public String toString() { int r = sync.getReadLockCount(); return super.toString() + "[Read locks = " + r + "]"; } }
读取锁是通过调用lock()方法来获取的,在这个方法中调用了acquireShared()方法,这个方法在AQS中实现,如下:
public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); }前面已经多次碰到这相同代码,不解释,直接来看tryAcquireShared()方法的实现:
protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); int c = getState(); // 获取锁的状态 // 如果锁是互斥锁,并且获取锁的线程不是当前线程,则返回-1,表示获取失败 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1; int r = sharedCount(c); // 获取读取锁的共享计数 // 如果不需要阻塞等待,并且读取锁的共享计数小于MAX_COUNT; // 则通过CAS函数更新锁的状态,将读取锁的共享计数+1。 if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { if (r == 0) { // 第1次获取读取锁 firstReader = current; firstReaderHoldCount = 1; // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程 } else if (firstReader == current) { firstReaderHoldCount++; } else { // HoldCounter是用来统计该线程获取读取锁的次数。 HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != current.getId()) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; // 将该线程获取读取锁的次数+1 } return 1; } return fullTryAcquireShared(current); }tryAcquireShared()的作用是尝试获取共享锁,如果通过如上的方法获取失败,则调用fullTryAcquireShared()方法来获取:
final int fullTryAcquireShared(Thread current) { HoldCounter rh = null; for (;;) { int c = getState(); // 获取锁的状态 if (exclusiveCount(c) != 0) { // 写线程获取互斥锁 if (getExclusiveOwnerThread() != current) // 获取锁的线程不是当前线程 return -1; } else if (readerShouldBlock()) { // 需要阻塞等待 if (firstReader == current) { // 当前线程是第一个线程 } else { if (rh == null) { rh = cachedHoldCounter; if (rh == null || rh.tid != current.getId()) { rh = readHolds.get(); if (rh.count == 0) readHolds.remove(); } } if (rh.count == 0)// 如果当前线程获取锁的计数为0,则返回-1。 return -1; } } // 不需要阻塞等待,获取读取锁的共享统计数;如果共享统计数超过MAX_COUNT,则抛出异常 if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 将线程获取读取锁的次数加1。 if (compareAndSetState(c, c + SHARED_UNIT)) { // 如果是第1次获取“读取锁”,则更新firstReader和firstReaderHoldCount。 if (sharedCount(c) == 0) { firstReader = current; firstReaderHoldCount = 1; // 如果想要获取锁的线程(current)是第1个获取锁(firstReader)的线程, // 则将firstReaderHoldCount+1。 } else if (firstReader == current) { firstReaderHoldCount++; } else { if (rh == null) rh = cachedHoldCounter; if (rh == null || rh.tid != current.getId()) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); // 更新线程的获取“读取锁”的共享计数 rh.count++; cachedHoldCounter = rh; // cache for release } return 1; } } }doAcquireShared()定义在AQS函数中,源码如下:
private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }这段代码与前面的实现相同,在此不估过多的解释。
2、释放共享读锁
调用ReadLock中的unlock()方法来释放共享读锁,在这个方法中调用了如下的方法:
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }
protected final boolean tryReleaseShared(int unused) { // 获取当前线程,即释放共享锁的线程 Thread current = Thread.currentThread(); // 如果想要释放锁的线程(current)是第1个获取锁(firstReader)的线程, // 并且第1个获取锁的线程获取锁的次数=1,则设置firstReader为null; // 否则,将第1个获取锁的线程的获取次数-1。 if (firstReader == current) { // assert firstReaderHoldCount > 0; if (firstReaderHoldCount == 1) firstReader = null; else firstReaderHoldCount--; } else { // 获取rh对象,并更新当前线程获取锁的信息 HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != current.getId()) rh = readHolds.get(); int count = rh.count; if (count <= 1) { readHolds.remove(); if (count <= 0) throw unmatchedUnlockException(); } --rh.count; } for (;;) { int c = getState(); // 获取锁的状态 int nextc = c - SHARED_UNIT;// 将锁的获取次数-1 // 通过CAS更新锁的状态 if (compareAndSetState(c, nextc)) return nextc == 0; } }
private void doReleaseShared() { for (;;) { // 获取CLH队列的头节点 Node h = head; // 如果头节点不为null,并且头节点不等于tail节点。 if (h != null && h != tail) { // 获取头节点对应的线程的状态 int ws = h.waitStatus; // 如果头节点对应的线程是SIGNAL状态,则意味着“头节点的下一个节点所对应的线程”需要被unpark唤醒。 if (ws == Node.SIGNAL) { // 设置“头节点对应的线程状态”为空状态。失败的话,则继续循环。 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // 唤醒“头节点的下一个节点所对应的线程”。 unparkSuccessor(h); } // 如果头节点对应的线程是空状态,则设置“文件点对应的线程所拥有的共享锁”为其它线程获取锁的空状态。 else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } // 如果头节点发生变化,则继续循环。否则,退出循环。 if (h == head) // loop if head changed break; } }
公平锁和非公平锁的区别,体现在判断是否需要阻塞的函数readerShouldBlock()是不同的。公平锁的readerShouldBlock()的源码如下:
final boolean readerShouldBlock() { return hasQueuedPredecessors(); }在公平共享锁中,如果在当前线程的前面有其他线程在等待获取共享锁,则返回true;否则,返回false。
final boolean readerShouldBlock() { return apparentlyFirstQueuedIsExclusive(); }在非公平共享锁中,它会无视当前线程的前面是否有其他线程在等待获取共享锁。只要该非公平共享锁对应的线程不为null,则返回true
4、举例
public class ReadWriteLockTest1 { public static void main(String[] args) { // 创建账户 MyCount myCount = new MyCount("4238920615242830", 10000); // 创建用户,并指定账户 User user = new User("Tommy", myCount); // 分别启动3个“读取账户金钱”的线程 和 3个“设置账户金钱”的线程 for (int i=0; i<3; i++) { user.getCash(); user.setCash((i+1)*1000); } } } class User { private String name; //用户名 private MyCount myCount; //所要操作的账户 private ReadWriteLock myLock; //执行操作所需的锁对象 User(String name, MyCount myCount) { this.name = name; this.myCount = myCount; this.myLock = new ReentrantReadWriteLock(); } public void getCash() { new Thread() { public void run() { myLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() +" getCash start"); myCount.getCash(); Thread.sleep(1); System.out.println(Thread.currentThread().getName() +" getCash end"); } catch (InterruptedException e) { } finally { myLock.readLock().unlock(); } } }.start(); } public void setCash(final int cash) { new Thread() { public void run() { myLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() +" setCash start"); myCount.setCash(cash); Thread.sleep(1); System.out.println(Thread.currentThread().getName() +" setCash end"); } catch (InterruptedException e) { } finally { myLock.writeLock().unlock(); } } }.start(); } } class MyCount { private String id; //账号 private int cash; //账户余额 MyCount(String id, int cash) { this.id = id; this.cash = cash; } public String getId() { return id; } public void setId(String id) { this.id = id; } public int getCash() { System.out.println(Thread.currentThread().getName() +" getCash cash="+ cash); return cash; } public void setCash(int cash) { System.out.println(Thread.currentThread().getName() +" setCash cash="+ cash); this.cash = cash; } }运行的结果如下:
Thread-0 getCash start Thread-2 getCash start Thread-0 getCash cash=10000 Thread-2 getCash cash=10000 Thread-0 getCash end Thread-2 getCash end Thread-1 setCash start Thread-1 setCash cash=1000 Thread-1 setCash end Thread-3 setCash start Thread-3 setCash cash=2000 Thread-3 setCash end Thread-4 getCash start Thread-4 getCash cash=2000 Thread-4 getCash end Thread-5 setCash start Thread-5 setCash cash=3000 Thread-5 setCash end
原文:http://blog.csdn.net/mazhimazh/article/details/19187839