JDK7中ReentrantReadWriteLock源码解析(2)
来源:互联网 发布:淘宝联盟 微信遭投诉 编辑:程序博客网 时间:2024/06/07 22:02
在阅读此篇文章前请确保对重入锁获取锁的源码有一定的了解,如果对重入锁获取锁源码不太清楚的可以参考:http://ericchunli.iteye.com/blog/2393222
ReentrantReadWriteLock中存在抽象内部类Sync,Sync用来实现所有的同步机制。这里提供读和写计数提取常量和方法,Lock的状态在逻辑上被切分为无符号的短整型两部分:低位代表独占写锁计数,高位代表共享读锁计数。
abstract static class Sync extends AbstractQueuedSynchronizer{
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;
static int sharedCount(int c){ return c>>>SHARED_SHIFT;} // 返回共享锁持有计数
static int exclusiveCount(int c){ return c&EXCLUSIVE_MASK;} // 返回独占锁持有计数
// ...省略其它代码
}
ReentrantReadWriteLock写锁的获取源码解析:
如果其它的线程没有获得读锁与写锁,则立即返回获得写锁,然后设置获得写锁持有数量为1。如果当前已经获得写锁则持有数量增加1后立即返回。如果锁被另一个线程持有,那么当前线程就会因为线程调度的目的而被禁用,并且处于休眠状态,直到获得了写锁,此时写入锁的计数将被设置为1。
public void lock(){
sync.acquire(1);
}
此处实现类似ReentrantLock获取锁的实现,只是tryAcquire(arg)实现不一致
public final void acquire(int arg){
if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquireQueued:获取在队列中已经存在的线程的独占的不可中断模式,被Condition的wait和acquire方法使用
addWaiter:给当前指定模式的线程创建节点,并且把当前节点添加到队列【队列不存在则创建】
selfInterrupt:中断当前线程以实现自我中断
对于尝试获取锁:如果读或者写的计数非0则表明锁被不同的线程持有,返回fail;如果计数将要饱和,返回fail,计数非0的时候才有可能发生;如果该线程是可重入的获取或遵循某种队列策略,则该线程可以被锁定,如果是则更新同步状态并且设置当前线程独占所有者。
protected final boolean tryAcquire(int acquires){
Thread current=Thread.currentThread();
int c=getState(); // 获取同步状态,AQS中的state
int w=exclusiveCount(c); // 返回独占锁的持有计数
if(c!=0){ // 不确定是什么线程获得什么锁,但是可以确定有线程获得锁
if(w==0||current!=getExclusiveOwnerThread())
// 其它线程获得读锁而导致共享锁计数不为0,其它线程获得写锁也会返回false
return false;
if(w+exclusiveCount(acquires)>MAX_COUNT)
throw new Error("Maximum lock count exceeded"); // 锁的技术不能够饱和
setState(c+acquires); // 写锁的重入,更新同步状态state的值
return true; // 返回尝试获取锁成功
}
// writerShouldBlock在公平锁和非公平锁中的实现是不相同的
// 可以参考:http://ericchunli.iteye.com/blog/2395841中的实现解析
// 如果需要阻塞或者state的值被改变则返回false
if(writerShouldBlock()||!compareAndSetState(c,c+acquires))
// 此处应该只能说明存在线程获得锁,但是什么线程获得什么锁应该是不确定的
return false;
// 设置当前拥有独占访问权的线程,null表示没有线程拥有访问权,此方法不会强制执行任何同步或volatile字段访问
setExclusiveOwnerThread(current);
return true;
}
ReentrantReadWriteLock写锁的释放源码解析:
如果当前线程是此锁的持有者则持有计数将递减,如果持有数现在为零则锁被释放。如果当前线程不是该锁的持有者则抛出IllegalMonitorStateException。
public void unlock() {
sync.release(1);
}
独占模式的释放,如果线程(可多个)实现了unblocking则返回true,可用于实现Lock#unlock方法
public final boolean release(int arg){
if(tryRelease(arg)){
Node h=head; // CLH队列节点
if(h!=null&&h.waitStatus!=0) // waitStatus用来表示当前节点状态
unparkSuccessor(h); // 如果存在后继节点则唤醒
return true;
}
return false;
}
tryRelease和tryAcquire能够被Conditions调用,因此它们的参数可能包含读和写,这些都是在condition等待和tryAcquire中的重新建立的过程中被释放
protected final boolean tryRelease(int releases){
if(!isHeldExclusively())
// getExclusiveOwnerThread()== Thread.currentThread();
// 虽然必须在所有者面前读取状态,但不需要检查当前线程是否为所有者
throw new IllegalMonitorStateException();
// 获取AQS的state的新值
int nextc=getState()-releases;
boolean free=exclusiveCount(nextc)==0; // 不为0表示存在重入
if(free)
setExclusiveOwnerThread(null); // 设置为null表示释放锁
setState(nextc); // 更新同步状态state的值
return free;
}
实现总结:ReentrantReadWriteLock.WriteLock是通过Sync来实现锁的获取与释放的,锁的获取对于公平锁和非公平锁是类似的,差异表现在锁的阻塞策略上,即writerShouldBlock的实现方式不一致,对于非公平锁而言,写锁总是优先获取,不考虑AQS队列中先来的线程;对于公平锁而言,需要判断是否存在等待更久的线程,同ReentrantLock实现一致。
ReentrantReadWriteLock.WriteLock通过获取同步状态(AQS中的state值),独占锁的持有计数,exclusiveOwnerThread来判断锁的获取【state!=0时的处理方式】,成功则返回true且更新同步状态state的值;也可通过writerShouldBlock()和CAS(state)的值来判断锁的获取,成功则返回true且更新同步状态state的值和设置当前线程为独占线程拥有者。
尝试获取失败则会把当前节点压入CLH队列,若CLH队列不为空则将新创建的Node添加到队列尾部,若CLH队列为空则新创建队列后把节点添加到队列中,Node节点压入队列后会重新尝试获得锁,可能会存在休眠或者取消时阻塞并返回中断状态【会导致自我中断的实现】,如果失败获取锁则会进行取消正在进行的获取的尝试,同ReentrantLock的实现一致。
ReentrantReadWriteLock.WriteLock通过获取同步状态state的值,独占锁的持有计数,exclusiveOwnerThread来实现锁的释放;无论锁在此处是否被释放,都需要重置同步状态state的值,如果独占锁的持有计数为0则会设置exclusiveOwnerThread为null【state为0】来实现锁的释放。
Note:相比ReentrantReadWriteLock.ReadLock而言,写锁的释放和获取还算简单,关于读锁的释放与获取,可参考http://ericchunli.iteye.com/blog/2396018。
- JDK7中ReentrantReadWriteLock源码解析(2)
- JDK7中ReentrantReadWriteLock源码解析(1)
- JDK7中ReentrantReadWriteLock源码解析(3)
- JDK7中ReentrantLock源码解析(2)
- JDK7中LockSupport源码解析
- JDK7中AtomicInteger源码解析
- JDK7中ArrayBlockingQueue源码解析
- JDK7中LinkedBlockingQueue源码解析
- JDK7中SynchronousQueue源码解析
- JDK7中StringBuffer/StringBuilder源码解析
- JDK7中ReentrantLock源码解析(1)
- JDK7中ReentrantLock源码解析(3)
- Java 1.7 ReentrantReadWriteLock源码解析
- JDK7中Condition源码概述
- JDK7中Lock源码概述
- JDK7中ReadWriteLock源码概述
- JDK7中Executor源码概述
- JDK7中ExecutorService源码概述
- 由三边求三角形面积
- Spring Boot 集成Redis
- 黑板架构风格
- 笨方法学习Python-习题15: 读取文件
- run custom shell script '[cp] copy pods resource
- JDK7中ReentrantReadWriteLock源码解析(2)
- 阿里云vpc+nat网关+路由表的详解
- 系统日志输出工具类
- systemctl命令
- 枚举---枚举的其他应用
- JDK7中ReentrantReadWriteLock源码解析(3)
- 简单的delegate代理使用
- MYSQL面试题整合
- leetcode 669. Trim a Binary Search Tree