赞
踩
设计的精髓:用一个变量如何维护多种状态
在 ReentrantLock 中,使用 Sync ( 实际是 AQS )的 int 类型的 state 来表示同步状态,表示锁被一个线程重复获取的次数。但是,读写锁 ReentrantReadWriteLock 内部维护着一对读写锁,如果要用一个变量维护多种状态,需要采用“按位切割使用”的方式来维护这个变量,将其切分为两部分:高16为表示读,低16为表示写。
分割之后,读写锁是如何迅速确定读锁和写锁的状态呢?通过位运算。假如当前同步状态为S,那么:
根据状态的划分能得出一个推论:S不等于0时,当写状态(S&0x0000FFFF)等于0时,则读状态(S>>>16)大于0,即读锁已被获取。
代码实现:java.util.concurrent.locks.ReentrantReadWriteLock.Sync
每个线程持有的读锁支持重入的特性,所以需要对每个线程持有的读锁的数量单独计数,这就需要用到 HoldCounter 计数器
读锁的内在机制其实就是一个共享锁
。一次共享锁的操作就相当于对HoldCounter 计数器的操作。获取共享锁,则该计数器 + 1,释放共享锁,该计数器 - 1
。只有当线程获取共享锁后才能对共享锁进行释放、重入操作。
通过 ThreadLocalHoldCounter 类,HoldCounter 与线程进行绑定。HoldCounter 是绑定线程的一个计数器,而 ThreadLocalHoldCounter 则是线程绑定的 ThreadLocal。
HoldCounter是用来记录读锁重入数的对象
ThreadLocalHoldCounter是ThreadLocal变量,用来存放不是第一个获取读锁的线程的其他线程的读锁重入数对象
写锁是一个支持重进入的排它锁
。如果当前线程已经获取了写锁,则增加写状态。如果当前线程在获取写锁时,读锁已经被获取(读状态不为0)或者该线程不是已经获取写锁的线程, 则当前线程进入等待状态。
写锁的获取是通过重写AQS中的tryAcquire方法实现的。
- protected final boolean tryAcquire(int acquires) {
- //当前线程
- Thread current = Thread.currentThread();
- //获取state状态 存在读锁或者写锁,状态就不为0
- int c = getState();
- //获取写锁的重入数
- int w = exclusiveCount(c);
- //当前同步状态state != 0,说明已经有其他线程获取了读锁或写锁
- if (c != 0) {
- // c!=0 && w==0 表示存在读锁
- // 当前存在读锁或者写锁已经被其他写线程获取,则写锁获取失败
- if (w == 0 || current != getExclusiveOwnerThread())
- return false;
- // 超出最大范围 65535
- if (w + exclusiveCount(acquires) > MAX_COUNT)
- throw new Error("Maximum lock count exceeded");
- //同步state状态
- setState(c + acquires);
- return true;
- }
- // writerShouldBlock有公平与非公平的实现, 非公平返回false,会尝试通过cas加锁
- //c==0 写锁未被任何线程获取,当前线程是否阻塞或者cas尝试获取锁
- if (writerShouldBlock() ||
- !compareAndSetState(c, c + acquires))
- return false;
-
- //设置写锁为当前线程所有
- setExclusiveOwnerThread(current);
- return true;
- 复制代码

通过源码我们可以知道:
写锁释放通过重写AQS的tryRelease方法实现
- protected final boolean tryRelease(int releases) {
- //若锁的持有者不是当前线程,抛出异常
- if (!isHeldExclusively())
- throw new IllegalMonitorStateException();
- int nextc = getState() - releases;
- //当前写状态是否为0,为0则释放写锁
- boolean free = exclusiveCount(nextc) == 0;
- if (free)
- setExclusiveOwnerThread(null);
- setState(nextc);
- return free;
- 复制代码
实现共享式同步组件的同步语义需要通过重写AQS的tryAcquireShared方法和tryReleaseShared方法。读锁的获取实现方法为:
- 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);
- /**
- * 读锁是否阻塞 readerShouldBlock()公平与非公平的实现
- * r < MAX_COUNT: 持有读锁的线程小于最大数(65535)
- * compareAndSetState(c, c + SHARED_UNIT) cas设置获取读锁线程的数量
- */
- if (!readerShouldBlock() &&
- r < MAX_COUNT &&
- compareAndSetState(c, c + SHARED_UNIT)) { //当前线程获取读锁
-
- if (r == 0) { //设置第一个获取读锁的线程
- firstReader = current;
- firstReaderHoldCount = 1; //设置第一个获取读锁线程的重入数
- } else if (firstReader == current) { // 表示第一个获取读锁的线程重入
- firstReaderHoldCount++;
- } else { // 非第一个获取读锁的线程
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
- cachedHoldCounter = rh = readHolds.get();
- else if (rh.count == 0)
- readHolds.set(rh);
- rh.count++; //记录其他获取读锁的线程的重入次数
- }
- return 1;
- }
- // 尝试通过自旋的方式获取读锁,实现了重入逻辑
- return fullTryAcquireShared(current);
- 复制代码

获取到读锁,执行完临界区后,要记得释放读锁(如果重入多次要释放对应的次数),不然会阻塞其他线程的写操作。
读锁释放的实现主要通过方法tryReleaseShared:
- protected final boolean tryReleaseShared(int unused) {
- Thread current = Thread.currentThread();
- //如果当前线程是第一个获取读锁的线程
- if (firstReader == current) {
- // assert firstReaderHoldCount > 0;
- if (firstReaderHoldCount == 1)
- firstReader = null;
- else
- firstReaderHoldCount--; //重入次数减1
- } else { //不是第一个获取读锁的线程
- HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
- rh = readHolds.get();
- int count = rh.count;
- if (count <= 1) {
- readHolds.remove();
- if (count <= 0)
- throw unmatchedUnlockException();
- }
- --rh.count; //重入次数减1
- }
- for (;;) { //cas更新同步状态
- int c = getState();
- int nextc = c - SHARED_UNIT;
- if (compareAndSetState(c, nextc))
- // Releasing the read lock has no effect on readers,
- // but it may allow waiting writers to proceed if
- // both read and write locks are now free.
- return nextc == 0;
- }
- 复制代码

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。