【JUC源码解析】ReentrantReadWriteLock

简介

ReentrantReadWriteLock, 可重入读写锁,包括公平锁和非公平锁,相比较公平锁而言,非公平锁有更好的吞吐量,但可能会出现队列里的线程无限期地推迟一个或多个读线程或写线程的情况,因为后来的线程不必入队等待就可以竞争锁。

概述

读写锁,分为读锁(共享锁)和写锁(独占锁),有两种模式,包括公平模式和非公平模式。

非公平模式

读写锁的默认模式,竞争到锁的线程是无序的,因为,后来者可能先抢到线程,这大大增加了吞吐量。

对于写锁而言,如果当前线程具备获得锁的条件,则可以直接闯入获取锁。

对于读锁而言,如果排在等待队列head结点(最后一个已经获得锁的线程结点)后面的一个结点,等待的是写锁(isShared() == false),那么,当前线程应该入队等待;如果等待的是读锁,那么,当前线程可以闯入尝试获取锁。

公平模式

竞争到锁的线程是有序的(线程的到达顺序),即,对于写锁而言,一定是队列里等待最久的线程得到写锁;对读锁而言,一定是队列里等待最久的线程得到读锁。

锁降级

重入允许写锁降级为读锁。

具体是,线程先获取写锁,然后在获取读锁,最后释放写锁。

没有锁升级,即,从读锁升级为写锁是不允许的。

中断

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

Condition

只能用于写锁。

应用

例一

 1 public class CachedData {
 2     Object data; // 缓存的数据
 3     volatile boolean cacheValid; // 缓存有效性
 4     final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); // 读写锁
 5 
 6     void processCachedData() {
 7         rwl.readLock().lock(); // 获取读锁
 8         if (!cacheValid) { // 如果缓存有效,直接使用数据,否则,释放读锁,并获取写锁
 9             rwl.readLock().unlock(); // 释放读锁
10             rwl.writeLock().lock(); // 获取读锁
11             try {
12                 if (!cacheValid) { // 再次判断数据有效性,读锁模式下,其他线程也许已经更新了缓存数据
13                     data = new Object(); // 更新缓存
14                     cacheValid = true; // 设置为有效
15                 }
16                 rwl.readLock().lock(); // 获取读锁
17             } finally {
18                 rwl.writeLock().unlock(); // 释放写锁
19             }
20         }
21 
22         try {
23             use(data); // 使用缓存
24         } finally {
25             rwl.readLock().unlock(); // 释放读锁
26         }
27     }
28 
29     private void use(Object data) { // 使用数据
30         System.out.println(data);
31     }
32 }

例二

 1 public class RWDictionary {
 2     private final Map<String, Data> m = new TreeMap<String, Data>(); // 集合
 3     private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); // 读写锁
 4     private final Lock r = rwl.readLock(); // 读锁
 5     private final Lock w = rwl.writeLock(); // 写锁
 6 
 7     public Data get(String key) { // 获取数据
 8         r.lock(); // 获取读锁
 9         try {
10             return m.get(key);
11         } finally {
12             r.unlock(); // 释放读锁
13         }
14     }
15 
16     public String[] allKeys() {
17         r.lock();
18         try {
19             return (String[]) m.keySet().toArray();
20         } finally {
21             r.unlock();
22         }
23     }
24 
25     public Data put(String key, Data value) { // 写数据
26         w.lock(); // 获取写锁
27         try {
28             return m.put(key, value);
29         } finally {
30             w.unlock(); // 释放写锁
31         }
32     }
33 
34     public void clear() {
35         w.lock();
36         try {
37             m.clear();
38         } finally {
39             w.unlock();
40         }
41     }
42 }
43 
44 class Data {
45 }

源码分析

属性

1     private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁
2     private final ReentrantReadWriteLock.WriteLock writerLock; // 写锁
3     final Sync sync; // 内部类,继承子AQS

构造方法

1     public ReentrantReadWriteLock() { // 构造方法
2         this(false); // 默认非公平模式
3     }
4 
5     public ReentrantReadWriteLock(boolean fair) { // 构造方法,根据fair觉得公平或非公平模式
6         sync = fair ? new FairSync() : new NonfairSync();
7         readerLock = new ReadLock(this);
8         writerLock = new WriteLock(this);
9     }

获取读写锁

1     public ReentrantReadWriteLock.WriteLock writeLock() {
2         return writerLock; // 写锁
3     }
4 
5     public ReentrantReadWriteLock.ReadLock readLock() {
6         return readerLock; // 读锁
7     }

内部类Sync

属性

 1         static final int SHARED_SHIFT = 16; // 高16位记录读锁的个数,低16位记录写锁的个数
 2         static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 读锁的单位,读锁减1,相当于减此单位值,写锁的单位(1)
 3         static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 锁的最大个数
 4         static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 写锁掩码,计算其个数时,与之相与,使高位为0,只计算低位
 5 
 6         static int sharedCount(int c) {// 计算读锁的个数
 7             return c >>> SHARED_SHIFT;
 8         }
 9 
10         static int exclusiveCount(int c) { // 计算写锁的个数
11             return c & EXCLUSIVE_MASK;
12         }
13 
14         static final class HoldCounter { // Sync的内部类,当前线程所持锁的计数器
15             int count = 0; // 个数
16             final long tid = getThreadId(Thread.currentThread()); // 线程ID
17         }
18 
19         static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
20             public HoldCounter initialValue() { // 初始值
21                 return new HoldCounter();
22             }
23         }
24 
25         private transient ThreadLocalHoldCounter readHolds; // 当前线程所持有的可重入读锁的计数器,减小到0时删除
26 
27         private transient HoldCounter cachedHoldCounter; // 缓存成功获取读锁的最后一个线程的计数器
28 
29         private transient Thread firstReader = null; // 第一个获得读锁的线程
30         private transient int firstReaderHoldCount; // 第一个获得读锁线程所持锁的个数

构造方法

1         Sync() { // 构造方法
2             readHolds = new ThreadLocalHoldCounter();
3             setState(getState());
4         }

是否入队

1         abstract boolean readerShouldBlock(); // 如果当前线程尝试获取读锁,是否让其等待
2 
3         abstract boolean writerShouldBlock(); // 如果当前线程尝试获取写锁,是否让其等待

 

 NonfairSync
1         final boolean writerShouldBlock() {
2             return false; // 非公平模式下,获取写锁的线程可以直接插队
3         }
4 
5         final boolean readerShouldBlock() { // 非公平模式下,如果队列中排在head结点的线程等待的是写锁,则,当前线程应该入队等待;如果是读锁,则可以插队
6             return apparentlyFirstQueuedIsExclusive();
7         }

 

FairSync
1         final boolean writerShouldBlock() {
2             return hasQueuedPredecessors(); // 公平模式下,如果等待队列里有线程在等待,则,当前线程应该入队等待
3         }
4 
5         final boolean readerShouldBlock() {
6             return hasQueuedPredecessors(); // 公平模式下,如果等待队列里有线程在等待,则,当前线程应该入队等待
7         }

 

获取写锁

 1         protected final boolean tryAcquire(int acquires) { // 获取写锁
 2             Thread current = Thread.currentThread(); // 当前线程
 3             int c = getState(); // 获取状态
 4             int w = exclusiveCount(c); // 计算写锁的状态
 5             if (c != 0) { // 如果当前状态不为0,说明有线程已经获得了读锁或写锁
 6                 if (w == 0 || current != getExclusiveOwnerThread())  // 如果有别的线程获取的是读锁(w == 0), 则返回false; 或者是有别的线程获取的是写锁,则查看那个线程是不是就是当前线程,如果不是,也返回false
 7                     return false;
 8                 if (w + exclusiveCount(acquires) > MAX_COUNT) // 到这一步,说明当前线程已经获取过写锁,这次是重入,如果超出最大值,抛出一个错误
 9                     throw new Error("Maximum lock count exceeded");
10                 setState(c + acquires); // 设置新的状态
11                 return true;
12             }
13             if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) // 如果当前状态为0,说明还没有线程获取过读锁或写锁;然而,如果需要其等待,而不是让其获取,则返回false;或者CAS设置状态失败(被其他线程抢先了),也返回false
14                 return false;
15             setExclusiveOwnerThread(current); // 设置当前线程为独占线程
16             return true; // 返回
17         }

释放写锁

 1         protected final boolean tryRelease(int releases) { // 释放写锁
 2             if (!isHeldExclusively()) // 如果当前线程没有持有锁,直接抛异常
 3                 throw new IllegalMonitorStateException();
 4             int nextc = getState() - releases; // 计算新的状态
 5             boolean free = exclusiveCount(nextc) == 0; // 计算写锁的空闲状态,如果没有线程持有写锁,表示空闲
 6             if (free) // 如果是空闲状态
 7                 setExclusiveOwnerThread(null); // 独占者线程为null
 8             setState(nextc); // 设置状态
 9             return free; // 返回
10         }

获取读锁

 1         protected final int tryAcquireShared(int unused) { // 获取读锁
 2             Thread current = Thread.currentThread(); // 获得当前线程
 3             int c = getState(); // 获取当前状态
 4             if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) // 如果别的线程持有写锁,直接返回失败(-1)
 5                 return -1;
 6             int r = sharedCount(c); // 获取读锁的个数
 7             if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { // 同时满足后面3个条件   1.不应该等待   2.小于最大值   3.CAS设置状态成功
 8                 if (r == 0) { // 如果读锁个数为0
 9                     firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
10                     firstReaderHoldCount = 1; // 个数设置为1
11                 } else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
12                     firstReaderHoldCount++; // 个数加1
13                 } else {
14                     HoldCounter rh = cachedHoldCounter; // 或取最后一个成功获取读锁的线程计数器
15                     if (rh == null || rh.tid != getThreadId(current)) // 如果为空,或者当前线程不是最后一个成功获取读锁的线程
16                         cachedHoldCounter = rh = readHolds.get(); // 则从ThreadLocal里获取一个(新的),并赋给缓存器
17                     else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
18                         readHolds.set(rh);
19                     rh.count++; // 个数加1
20                 }
21                 return 1; // 返回成功(1)
22             }
23             return fullTryAcquireShared(current); // 最后,完整版本的尝试获取
24         }

 

完整版本的尝试获取读锁

 1         final int fullTryAcquireShared(Thread current) { // 完整版本的尝试获取读锁
 2             HoldCounter rh = null; // 线程锁计数器
 3             for (;;) {
 4                 int c = getState(); // 获取当前状态
 5                 if (exclusiveCount(c) != 0) { // 如果有线程持有写锁
 6                     if (getExclusiveOwnerThread() != current) // 判断持有写锁的线程是否是当前线程,如果不是,返回失败(-1)
 7                         return -1;
 8                 } else if (readerShouldBlock()) { // 如果应该等待
 9                     if (firstReader == current) {
10                     } else { // 当前线程不是第一个获得读锁的线程
11                         if (rh == null) { // 线程计数器为空
12                             rh = cachedHoldCounter; // 获取缓存的,最后一次成功获得读锁的线程计数器
13                             if (rh == null || rh.tid != getThreadId(current)) { // 为空,或者不是当前线程的计数器
14                                 rh = readHolds.get(); // 从线程变量里获取
15                                 if (rh.count == 0) // 如果个数为0
16                                     readHolds.remove(); // 从线程变量里删除
17                             }
18                         }
19                         if (rh.count == 0) // 如果个数为0,返回失败(-1)
20                             return -1;
21                     }
22                 }
23                 if (sharedCount(c) == MAX_COUNT) // 个数达到最大值,抛出一个错误
24                     throw new Error("Maximum lock count exceeded");
25                 if (compareAndSetState(c, c + SHARED_UNIT)) { // CAS设置新的状态
26                     if (sharedCount(c) == 0) { // 如果读锁的个数为0
27                         firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
28                         firstReaderHoldCount = 1; // 个数设置为1
29                     } else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
30                         firstReaderHoldCount++; // 个数加1
31                     } else {
32                         if (rh == null)
33                             rh = cachedHoldCounter; // 从缓存中获取计数器
34                         if (rh == null || rh.tid != getThreadId(current)) // 为空,或者不是当前线程的计数器
35                             rh = readHolds.get(); // 从线程变量里获取
36                         else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
37                             readHolds.set(rh);
38                         rh.count++; // 个数加1
39                         cachedHoldCounter = rh; // 缓存等待释放
40                     }
41                     return 1; // 返回成功(-1)
42                 }
43             }
44         }

释放读锁

 1         protected final boolean tryReleaseShared(int unused) { // 释放读锁
 2             Thread current = Thread.currentThread(); // 获得当前线程
 3             if (firstReader == current) { // 如果当前线程是第一个获的读锁的线程
 4                 if (firstReaderHoldCount == 1) // 如果所持锁的个数为1
 5                     firstReader = null; // 置空
 6                 else
 7                     firstReaderHoldCount--; // 否则,递减
 8             } else { // 如果不是
 9                 HoldCounter rh = cachedHoldCounter; // 获取最后成功获得读锁的计数器,缓存的目的是避免从ThreadLocal里取值
10                 if (rh == null || rh.tid != getThreadId(current)) // 如果为空,或者不是当前线程的计数器,也即,当前线程不是成功获得读锁的线程
11                     rh = readHolds.get(); // 获取当前线程的技术器
12                 int count = rh.count; // 获取当前线程所持锁的个数
13                 if (count <= 1) { // 如果个数小于或等于1
14                     readHolds.remove(); // 删除当前线程的计数器
15                     if (count <= 0) // 如果小于0,抛出异常
16                         throw unmatchedUnlockException();
17                 }
18                 --rh.count; // 计数减1
19             }
20             for (;;) {
21                 int c = getState(); // 获取当前状态
22                 int nextc = c - SHARED_UNIT; // 计算新的状态
23                 if (compareAndSetState(c, nextc)) // CAS设置新的状态
24                     return nextc == 0; // 返回
25             }
26         }

尝试获取写锁

 1         final boolean tryWriteLock() { // 尝试获取写锁
 2             Thread current = Thread.currentThread(); // 获取当前线程
 3             int c = getState(); // 获取当前状态
 4             if (c != 0) { // 如果有线程获取过锁,读锁或写锁
 5                 int w = exclusiveCount(c); // 计算写锁的个数
 6                 if (w == 0 || current != getExclusiveOwnerThread()) // 如果别的线程获取的是读锁,返回false; 或者有线程获取的是写锁,但那个线程不是当前线程,返回false
 7                     return false;
 8                 if (w == MAX_COUNT) // 超过最大值,抛出一个错误
 9                     throw new Error("Maximum lock count exceeded");
10             }
11             if (!compareAndSetState(c, c + 1)) // CAS 状态加1
12                 return false;
13             setExclusiveOwnerThread(current); // 设置当前线程为独占者线程
14             return true;
15         }

 

尝试获取读锁

 1         final boolean tryReadLock() { // 尝试获取读锁
 2             Thread current = Thread.currentThread(); // 获取当前线程
 3             for (;;) {
 4                 int c = getState(); // 获取当前状态
 5                 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) // 如果有别的线程获取了写锁,直接返回失败
 6                     return false;
 7                 int r = sharedCount(c); // 获取读锁的个数
 8                 if (r == MAX_COUNT) // 达到最大值,抛出一个错误
 9                     throw new Error("Maximum lock count exceeded");
10                 if (compareAndSetState(c, c + SHARED_UNIT)) { // CAS设置状态的值
11                     if (r == 0) { // 如果读锁的个数为0
12                         firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
13                         firstReaderHoldCount = 1; // 个数设置为1
14                     } else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
15                         firstReaderHoldCount++; // 个数加1
16                     } else {
17                         HoldCounter rh = cachedHoldCounter; // 从缓存中获取计数器
18                         if (rh == null || rh.tid != getThreadId(current)) // 为空,或者不是当前线程的计数器
19                             cachedHoldCounter = rh = readHolds.get(); // 则从ThreadLocal里获取一个(新的),并赋给缓存器
20                         else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
21                             readHolds.set(rh);
22                         rh.count++; // 个数加1
23                     }
24                     return true; // 返回
25                 }
26             }
27         }

 

实现读写锁

例子来自网上。

 1 public final class ReadWriteLock {
 2     private int readingReaders = 0; // 获得锁的读线程个数
 3     private int writingWriters = 0; // 获得锁的写线程个数,最多1个
 4     private int waitingWriters = 0; // 等待锁的写线程个数
 5     private boolean preferWriter = true; // 偏向写线程,当写线程释放锁的时候,才为false
 6 
 7     public synchronized void readLock() throws InterruptedException { // 读锁
 8         while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) { // 如果写线程获得锁,或者,等待锁的写线程个数大于0,而当前又偏向写线程的情况
 9             wait(); // 则当前线程释放锁,进入等待队列,等待被唤醒,并重新抢占锁
10         }
11         readingReaders++; // 读线程个数加1
12     }
13 
14     public synchronized void readUnlock() { // 释放读锁
15         readingReaders--; // 读线程个数减1
16         preferWriter = true; // 偏向写线程
17         notifyAll(); // 唤醒所有等待的线程,包括读线程和写线程
18     }
19 
20     public synchronized void writeLock() throws InterruptedException { // 写锁
21         waitingWriters++; // 等待锁的写线程个数加1
22         try {
23             while (readingReaders > 0 || writingWriters > 0) { // 读锁或写锁个数大于0,则入队等待
24                 wait();
25             }
26         } finally {
27             waitingWriters--; // 等待锁的写线程个数减1
28         }
29         writingWriters++; // 写线程个数加1
30     }
31 
32     public synchronized void writeUnlock() {
33         writingWriters--; // 线程个数减1
34         preferWriter = false; // 不偏向写线程
35         notifyAll(); // 唤醒所有等待的线程,包括读线程和写线程
36     }
37 }

 

 

行文至此结束。

 

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_rrwl.html

posted @ 2018-03-07 14:20  林城画序  阅读(216)  评论(0编辑  收藏  举报