MyException - 我的异常网
当前位置:我的异常网» 互联网 » JDK7中ReentrantReadWriteLock源码解析(二)

JDK7中ReentrantReadWriteLock源码解析(二)

www.MyException.Cn  网友分享于:2013-10-27  浏览:0次
JDK7中ReentrantReadWriteLock源码解析(2)

在阅读此篇文章前请确保对重入锁获取锁的源码有一定的了解,如果对重入锁获取锁源码不太清楚的可以参考: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。

文章评论

那些争议最大的编程观点
那些争议最大的编程观点
如何成为一名黑客
如何成为一名黑客
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
10个调试和排错的小建议
10个调试和排错的小建议
程序员的鄙视链
程序员的鄙视链
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
代码女神横空出世
代码女神横空出世
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
旅行,写作,编程
旅行,写作,编程
每天工作4小时的程序员
每天工作4小时的程序员
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
鲜为人知的编程真相
鲜为人知的编程真相
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
为什么程序员都是夜猫子
为什么程序员都是夜猫子
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
我的丈夫是个程序员
我的丈夫是个程序员
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
编程语言是女人
编程语言是女人
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
总结2014中国互联网十大段子
总结2014中国互联网十大段子
老程序员的下场
老程序员的下场
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
我是如何打败拖延症的
我是如何打败拖延症的
程序员都该阅读的书
程序员都该阅读的书
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
漫画:程序员的工作
漫画:程序员的工作
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
一个程序员的时间管理
一个程序员的时间管理
Java程序员必看电影
Java程序员必看电影
 程序员的样子
程序员的样子
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有