AQS之ReentrantReadWriteLock精讲分析上篇

1.用法 1.1 定义一个安全的list集合 public class LockDemo { ArrayList<Integer> arrayList = new ArrayList<>();//定义一个集合 // 定义读锁 ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock(true).readLock(); // 定义写锁 ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock(true).writeLock(); public void addEle(Integer ele) { writeLock.lock(); // 获取写锁 arrayList.add(ele); writeLock.unlock(); // 释放写锁 } public Integer getEle(Integer index) { try{ readLock.lock(); // 获取读锁 Integer res = arrayList.get(index); return res; } finally{ readLock.unlock();// 释放读锁 } } } 1.2 Sync类中的源码

Sync类中属性介绍

abstract static class Sync extends AbstractQueuedSynchronizer {     // 高16位为读锁,低16位为写锁     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;     // 本地线程计数器     private transient ThreadLocalHoldCounter readHolds;     // 缓存的计数器     private transient HoldCounter cachedHoldCounter;     // 第一个读线程     private transient Thread firstReader = null;     // 第一个读线程的计数     private transient int firstReaderHoldCount; }

Sync类中计数相关类

// 计数器 static final class HoldCounter { int count = 0; // 计数 // 获取当前线程的TID属性的值 final long tid = getThreadId(Thread.currentThread()); }

HoldCounter主要有两个属性,count和tid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程

// 本地线程计数器 static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { // 重写初始化方法,在没有进行set的情况下,获取的都是该HoldCounter值 public HoldCounter initialValue() { return new HoldCounter(); } }

ThreadLocalHoldCounter重写了ThreadLocal的initialValue方法,ThreadLocal类可以将线程与对象相关联。在没有进行set的情况下,get到的均是initialValue方法里面生成的那个HolderCounter对象
Sync类中构造函数

// 构造函数 Sync() {     // 本地线程计数器     readHolds = new ThreadLocalHoldCounter();     // 设置AQS的状态     setState(getState()); } 2.获取读锁源码分析 2.1 读锁加锁分析

先看读锁操作 readLock.lock(), 获取读取锁定

如果写锁未被另一个线程持有,则获取读锁并立即返回。

如果写锁由另一个线程持有,将当前线程将被阻塞,并处于休眠状态,直到获取读锁为止。

public void lock() { sync.acquireShared(1); }

以共享模式获取,此方法不支持中断。 通过首先至少调用一次tryAcquireShared ,并在成功后返回。 否则,线程将排队,并可能反复阻塞和解除阻塞,并调用tryAcquireShared直到成功。

返回负数表示获取失败

返回0表示成功,但是后继争用线程不会成功

返回正数表示获取成功,并且后继争用线程也可能成功

public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } 2.2 tryAcquireShared 获取锁分析 protected final int tryAcquireShared(int unused) { // 获取当前线程 Thread current = Thread.currentThread(); // 获取状态 int c = getState(); /** 计算独占的持有次数 static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } */ // exclusiveCount(c) 第一次返回的是0 // 如果写锁线程数不等于0,并且独占锁不是当前线程则返回失败 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1; /** 计算共享的持有次数 直接将state右移16位,就可以得到读锁的线程数量 static int sharedCount(int c) { return c >>> SHARED_SHIFT; } */ // sharedCount(c) 第一次返回的是0 // 读锁的数量 int r = sharedCount(c); //readerShouldBlock() 当前读线程是否堵塞 if (!readerShouldBlock() && // 持有线程小于最大数65535 r < MAX_COUNT && // 设置读取锁状态 compareAndSetState(c, c + SHARED_UNIT)) { if (r == 0) { // firstReader是第一个获得读取锁定的线程, // 第一个读锁firstReader是不会加入到readHolds中 firstReader = current; // firstReaderHoldCount是firstReader的保留计数也就是 // 读线程占用的资源数为1 firstReaderHoldCount = 1; // 如果第一个读线程是当前线程那么就将计数+1 } else if (firstReader == current) { firstReaderHoldCount++; } else { // 读锁数量不为0并且不为当前线程 // 每个线程读取保持计数的计数器。 维护为ThreadLocal // 缓存在cachedHoldCounter中 HoldCounter rh = cachedHoldCounter; // 计数器为空或者计数器的tid不为当前正在运行的线程的tid if (rh == null || rh.tid != getThreadId(current)) // 获取当前线程对应的计数器 cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) // 计数为0 // 加入到readHolds中 readHolds.set(rh); rh.count++; // +1 } // 获取锁成功 return 1; } return fullTryAcquireShared(current); }

readerShouldBlock()

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppjjx.html