Java中的ReentrantLock的原理和部分源码分析
最近在研究jdk中的锁。下面我们说一下。
Java并发包中提供了Lock接口用来实现锁功能。提供了与synchronized类似的同步功能。只是要在使用时需要显示地获取锁和释放锁,虽然缺少隐式获取锁的便捷性,但是拥有
锁获取与释放的可操作性性,可中断的获取锁以及超时获取锁等多种synchronized不具备的同步特性。
1、队列同步器
队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基本框架,使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态。
下面我们来说一下 独占式 获取锁状态的原理
独占式获取锁会导致其他线程争抢这个一个锁资源的时候被阻塞
ReentrantLock------>调用lock()----------默认非公平锁------->在ReentrantLock内部实现的非公平同步器NonfairSync中调用----lock()如下图
如果获取锁失败--->调用父类AbstractQueuedSynchronizer同步器中的acquire(int arg)方法如图
上图的逻辑:我尝试获取下锁,如果能获取到,后面的acquireQueued()就不用执行,直接拿到锁,如果没有拿到锁,返回false。那么就会执行后面的将当前线程构造成一个节点Node放到同步队列中。我们看下如何tryAcquire(arg)
tryAcquire(arg)内部就调用了上图的这个方法。说明看图里的内容。---------->如果返回fasle------就要将当前线程构造成一个Node节点放到同步队列里---->添加节点的逻辑如下图
acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 里的addWaiter()方法就是将当前线程构造成一个节点Node.放到同步队列中,Node就是当前线程的一个引用。
acquireQueued()。获取队列,是通过自旋来判断该node何时拿到锁并返回
如何做到阻塞呢?其实线程在没有拿到锁的时候,其实是一直在运行的,一直在跑上面的自旋判断,没拿到锁就相当于一直阻塞在队列里并一直循环判断。停在了lock()锁获取这里
以上是非公平锁的实现原理;
下面我看下公平锁的实现。
公平锁与非公平锁的区别就只是在调用公平锁的时候,没有直接CAS原子操作去尝试设置修改状态拿锁,而是,直接调用的acquire(1)--如图
锁的释放:
大致原理:获取到同步器的state值 c,然后减去1或者传过来的参数arg.,如果c-1=0或者c-arg=0;那么就将同步器所持的线程置为null.并将c设置成0;如图源码
这就是独占式锁的大致原理。如有不对的地方请指导。
--我觉得同步队列还是有必要研究下的。