上一篇博客更新了AQS的一个源码解析,从中我们可以了解到,AQS仅仅依靠一个int值就可以实现线程阻塞排队以及执行的机制。
依赖AQS实现的锁有很多种,ReentrantLock就是其中的一个。下篇博客就是将分析另一种锁ReentrantReadWriteLock。这种锁涉及到写锁与读锁。而AQS仅仅单靠一个int值就区分了读锁。为了理解源码简单,我在这里先将这部分知识点讲解一下。
/*
* Read vs write count extraction constants and functions.
* Lock state is logically divided into two unsigned shorts:
* The lower one representing the exclusive (writer) lock hold count,
* and the upper the shared (reader) hold count.
*/
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
这一部分代码就是关于AQS使用单一int值表示读锁与写锁两种状态,现在就来分析一下实现的原理吧
首先,我们都知道一个int的值是32位。AQS将这32位分为高16位与低16位两个无符号的short值。(这里我们讨论的都是无符号的数值,下面不再强调)。高是16位用来表示读锁(共享锁),低16位用来表示写锁(独占锁)
读锁(共享锁) | 写锁(独占锁) |
---|---|
0000000000000000 | 0000000000000000 |
最大的共享数MAX_COUNT为1左移16位减一,也就是二进制为00000000000000010000000000000000转换成十进制为65536再减去1位65535。同理最大独占数也为65535。
那么AQS又是如何去获取读锁的个数呢? 通过sharedCount(int c) 我们可以知道了解到将一个整数无符号右移16位,从而得到读锁数。具体看下表转换过程
1、假设现在有一个读锁
读锁(共享锁) | |
---|---|
0000000000000001 | 0000000000000000 |
2、现在进行读锁个数加一(由于高16位表示读锁,所以每次新增一个读锁,都是在当前状态上加65536)
读锁(共享锁) | |
---|---|
0000000000000001 | 0000000000000000 |
0000000000000001 | 0000000000000000 |
0000000000000010 | 0000000000000000 |
3、现在获取读锁个数需进行无符号右移16位,变成如下二进制
读锁(共享锁) | |
---|---|
0000000000000000 | 0000000000000010 |
将这个二进制转换成十进制可得十进制数2。
这就是读锁的操作过程。接下来看看写锁是如何进行操作的
1、假设现在有一个写锁
写锁(独占锁) | |
---|---|
0000000000000000 | 0000000000000001 |
2、现在进行写锁加1(由于低16位表示写锁,所以每新增一个写锁,都是在当前状态上直接加1)
写锁(独占锁) | |
---|---|
0000000000000000 | 0000000000000001 |
0000000000000000 | 0000000000000001 |
0000000000000000 | 0000000000000010 |
3、现在获取写锁的个数,通过exclusiveCount(int c)方法可以知道,将int c与上65535,从而得到写锁个数
写锁(独占锁) | |
---|---|
0000000000000000 | 0000000000000010 |
0000000000000000 | 111111111111111111 |
0000000000000000 | 0000000000000010 |
从上可以看到读锁与写锁的操作过程。从这里我们可以看到,AQS在处理读锁与写锁的方式很巧妙,永远把高位的16位变成0,从而消除高位的影响。这里是非常值得借鉴的地方。
PS:这一篇博客的内容介绍是为了下一篇关于ReentrantReadWriteLock的源码阅读进行一个知识补充。由于ReentrantReadWriteLock的内容有点多,我准备分成三到四篇博客进行分析。敬请期待。。。。