(十)JDK源码分析之ReentrantLock流程总结

  • 建议
    希望读者打开源码对照看,或者有AQS源码基础存在一些疑惑的同学看,一定能有所收获

  • lock
    1.调用AQS的acquire方法,先tryAcquire方法尝试获取锁根据status的判断。如果获取锁成功,那么直接返回结束;如果尝试获取失败,那么需要将当前线程封装为Node对象,加入到同步队列中。

    2.如果尝试获取锁失败,那么在acquire继续调用addWaiter方法将当前线程封装为Node对象,加入到同步队列中,如果同步队列头结点没有初始化,那么先cas初始化头结点,然后再cas将当前创建的Node加入到尾节点后面。

    3.将上面未获取到的Node添加到阻塞队列后,在acquire方法中,会继续调用acquireQueued方法进行轮训获取锁,开始自旋(死循环)。1.自旋尝试获取锁是再次调用tryAcquire方法,但是尝试获取锁是有条件的,必须当前Node的前面的Node是头结点,才会去tryAcquire,否则是在浪费,因为你前面Node都没获取到锁,你怎么可能获取的到呢。2.自旋最多一次执行两次,如果第二次自旋还是没获取到锁,那么当前线程被挂起(LockSupport.park),一直阻塞,直到被唤醒。

    4.被唤醒时,其实老头结点还在(注意,注意,注意)。被唤醒的节点,在获取到了锁之后,才会真正把老头结点释放掉,然后将自己设置为新的头结点。

  • unlock
    1.调用release开始释放锁,调用tryRelease修改status值。比如将status值减一,将exclusiveOwnerThread也就是当前AQS中持有锁的线程置为null.
    2.在release方法中会调用unparkSuccessor方法进行唤醒(LockSupport.unpark)他的下一个节点,让他从挂起的状态中唤醒,再次自旋去获取锁。

  • 总结
    1.大概流程就是一个线程去获取锁,如果获取成功,那么结束,如果获取失败,那么加在同步队列中,然后自旋两次,直到前驱节点释放锁唤醒后继节点,被唤醒后继续去获取锁。

    2 在实现是有公平锁和非公平锁之分,这个在tryAcquire中有体现。公平锁,去尝试获取锁会先调用hasQueuedPredecessors,判断同步队列中是否有等待的Node,如果有,那么直接排队去,不参与锁的竞争;非公平锁,就是即使你是排队在同步队列中第一个,那么新加入的线程依然会和它竞争。也就是说在被前驱节点唤醒的线程,开始再次自旋获取锁时,公平锁时,是一定可以获取到锁的,非公平锁不一定能获取到,可能在竞争激烈情况下,造成锁获取饥饿。

    3由于可重入锁的特点,status的值可能会大于1,同一个线程可以获取多次锁,每次加1,也就是说一个线程,你可以调用多次lock,但是同时几点对应调用多次unlock,因为只要status>0,那么还是那个线程持有锁

    4.头结点唤醒后继节点的一些细节需要注意一下。对于公平锁,在头结点释放锁之后,后继节点可以马上获取到锁,将自己设置为新的头结点;对于非公平锁,在头结点释放锁,虽然唤醒了后继节点,但是有新进来(非同步队列中)的线程可能会抢到锁,导致后继节点获取锁失败,再次被挂起,当新进来的线程释放锁时,会使用头结点再次唤醒它的后继节点(新进来的队列直接就获取到锁了,没在同步队列中,所以不需要去做acquireQueued的这段逻辑,只要在释放锁时,再次唤醒头结点的后继节点即可)。

    5.公平锁和非公平锁的区别只是在入队列前头结点的下一个节点(新头结点)会和非同步队列的线程发生竞争,入队列后是一样的。

    6.纠正一句话,“头结点是持有锁的节点”。公平锁时,这句话没啥问题,非公平锁时,持有锁的节点可能是头结点,也有可能是入队前竞争的那个线程抢到了锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值