一、Lock锁具体是如何实现的
由上一篇博客解释,实现Lock锁的子类,实现了接口的所有方法。每个方法又都依赖Sync这个内部静态类来实现的,所以主要看一下Sync这个内部静态类。
abstract static class Sync extends AbstractQueuedSynchronizer
Sync继承了AbstractQueuedSynchronizer这个抽象类,其实它是java语言中一个重要的队列同步器,简称AQS。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等)。
下面先来看一下AQS中和Lock锁有关的一些方法:
独占锁(ReentrantLock)本篇讲解独占锁
void acquire(int arg) //独占式获取同步状态,如果获取失败则插入同步队列进行等待。
void acquireInterruptibly(int arg) //与acquire方法相同,但在同步队列中等待时可以响应中断。
boolean tryAcquireNanos(int arg,long nanosTimeout) //在2的基础上增加了超时等待功能,在超时时间内没有获 得同步状态返回false
boolean tryAcquire(int arg) //获取锁成功返回true,否则返回false
boolean release(int arg) //释放同步状态,该方法会唤醒在同步队列中的下一个节点。
共享锁(ReentrantReadWriteLocK)
void acquireShared(int arg) //共享式获取同步状态,与独占锁的区别在于同一时刻有多个线程获取同步状态。
void acquireSharedInterruptibly(int arg) //增加了响应中断的功能
boolean tryAcquireSharedNanos(int arg,lone nanosTimeout) //在2的基础上增加了超时等待功能
boolean releaseShared(int arg) //共享锁释放同步状态。
上面代码的解析中提到了一个同步队列,这个队列就是来管理那些同时竞争一个锁的时候,没有竞争到锁的线程,会进行排队放在一个数据结构中进行管理。那么这个数据结构是怎样的形式存在的?
在AQS有一个静态内部类Node,这是我们同步队列的每个具体节点。在这个类中有如下属性
volatile int waitStatus; // 节点状态
volatile Node prev; // 当前节点的前驱节点
volatile Node next; // 当前节点的后继节点
volatile Thread thread; // 当前节点所包装的线程对象
Node nextWaiter; // 等待队列中的下一个节点
可以初步推断出这些没有竞争到锁的线程,会被封装成一个节点,然后以双向链表的形式管理起来。
节点状态是由一个简单的int型来保存的,用来表示此线程目前所处的状态:
int INITIAL = 0; // 初始状态
int CANCELLED = 1; // 当前节点从同步队列中取消
int SIGNAL = -1; // 后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,使得后继 节点的线程继续运行。
int CONDITION = -2; // 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了 signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到对同步状态的获取中。
int PROPAGATE = -3; // 表示下一次共享式同步状态获取将会无条件地被传播下去。
在队列中的管理,主要依靠这个int型的变量。
另外AQS中有两个重要的成员变量:
private transient volatile Node head;
private transient volatile Node tail;
由此可知,整个双向链表是由头尾节点来管理的。这样更加方便线程的入队和出队操作。
那么,节点如何进行入队和出队操作?实际上这对应着锁的获取和释放两个操作:获取锁失败进行入队操作,获取 锁成功进行出队操作。
二、独占锁ReentrantLock
现在我们来看一下ReentrantLock中的一些加锁和解锁操作的源码是怎样的。
1.独占锁的获取
调用lock()方法是获取独占锁,获取失败就将当前线程加入同步队列,成功 则线程执行。来看ReentrantLock源码:
public void lock() {
sync.lock();
}
ReentrantLock的lock调用了sync的lock,我们看看sync是怎么来的:
//构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}