AbstractQueuedSynchronizer与ReentrantLock。排他锁实现分析
来源:互联网 发布:java环境一键安装包 编辑:程序博客网 时间:2024/05/16 00:27
一篇较全的文章:http://ifeve.com/introduce-abstractqueuedsynchronizer/。另外并发编程网也有关于aqs的doug lea的论文翻译,可以参考。
AbstractQueuedSynchronizer关键方法
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }这方法进行了关键性的一步,setHead的时候,解除了前驱节点关联,同时p.next=null解除了后驱节点的关联。当一个节点被设置为头节点的时候,会解除对前驱节点的关联。
这里应该是整个排他锁实现比较关键的一步了。
在此方法之前的步骤基本没有什么特别之处,基本上是通过不断的cas,让自己成为一个尾节点(中间过程可能包括初始化头节点,尾节点enq),并且和之前的节点建立双向关联。
看这里的for(;;),之前让我产生误解的地方,开始误以为这里既是论文中提到的自旋锁过程。其实这里是一个简单的重试,这里的for也为非公平锁提供了一个闯入的机会。一旦某个线程执行到这一步是不可能具有非公平锁的,这里是指给其他线程提供了闯入的机会。
来看看如何闯入
ReentrantLock中非公平锁尝试获取锁的代码:
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
getState()根据aqs的api描述,是自定义含义的。在ReentrantLock中,其实这里代表了是否有线程正在进行,或者说,是否有线程拿到了锁。这里和公平锁最大的区别在于是否判定有等待队列。只要发现getState值为0,则,立即开始尝试更新state的值,一旦更新成功这会导致任何公平或者非公平的非公平锁请求直接进入chl队列。
返回上面提到说acquireQueued方法提供了闯入机会,是因为调用逻辑中,如果shouldParkAfterFailedAcquire(p,node) && parkAndCheckInterrupt() 成功执行,那么线程会block住,等待上个线程释放锁最终调用LockSupport.unpark(t),那么这个线程才会继续开始运行。在刚被唤醒的时候,这个时候只要acquireQueued中调用tryAcquire(arg)没有成功执行(准确的说是state变量没被成功设值)。那么非公平锁,就有机会提前抢到执行机会。整个过程中,aqs中的state变量担当了重要角色。
还有一个非锁竞争关系的问题,如果释放锁,刚好调用到release(int arg) 调用unparkSuccessor(h),unparkSuccessor(h)又刚好执行完,另外一个线程又才进入队列也设置了上个节点等待状态为Node.SIGNAL,准备执行if (shouldParkAfterFailedAcquire(p,node) && parkAndCheckInterrupt())。当执行shouldParkAfterFailedAcquire以后必然调用 LockSupport.park(this);问题来了,岂不是这个线程会永远的block?不会block的原因在于,unparkSuccessor(h)中如果提前调用了 LockSupport.unpark(s.thread);那么会导致随后的LockSupport.park方法不会block线程。
- AbstractQueuedSynchronizer与ReentrantLock。排他锁实现分析
- 源码分析----ReentrantLock实现和AbstractQueuedSynchronizer
- 从ReentrantLock去分析AbstractQueuedSynchronizer
- JDK之ReentrantLock,AbstractQueuedSynchronizer源码分析
- ReentrantLock之AbstractQueuedSynchronizer 源码分析笔记
- AbstractQueuedSynchronizer的实现分析
- 排他锁 案例分析
- Lock、ReentrantLock和AbstractQueuedSynchronizer的源码要点分析整理
- Lock、ReentrantLock和AbstractQueuedSynchronizer的源码要点分析整理
- AbstractQueuedSynchronizer详解(一)——分析ReentrantLock源码
- ReentrantLock、AbstractQueuedSynchronizer解读
- java AbstractQueuedSynchronizer的实现分析(独占锁)
- java AbstractQueuedSynchronizer的实现分析(共享锁)
- 深入分析AbstractQueuedSynchronizer独占锁的实现原理:ReentranLock
- 深入分析AbstractQueuedSynchronizer共享锁的实现原理:CountDownLatch
- ReentrantLock实现原理分析
- ReentrantLock实现原理分析
- 可重入锁 ReentrantLock 源码解读 (2)锁框架 AbstractQueuedSynchronizer
- web开发工具 http-server , grunt 使用
- Lucene.net站内搜索3—最简单搜索引擎代码
- Leetcode: Pascal's Triangle
- DB link的迁移(dblink中的密码未知)
- Quick-cocos2d-x的MVC架构之model研究
- AbstractQueuedSynchronizer与ReentrantLock。排他锁实现分析
- 乘法逆元的快捷求法
- C++ 访问控制和继承private、public、protected
- Head First Python Notes - Chapter 6
- arttemplate js模板的使用
- Linux Shell环境变量
- Mac 登陆vpn 却上不去网的解决方法
- 如何投资
- 硬盘寻址能力的变换