上一篇说到的显示锁(Lock),并且知道显示锁比synchronized更加灵活,ReentrantLock作为Lock的实现类,这一篇看下ReentrantLock的源码。
ReentrantLock类:
构造方法:可以直接通过构造器创建ReentrantLock对象,公平锁和非公平锁在之后会有介绍
/**默认构造器:非公平锁*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**带参构造器:通过fair设置公平锁或非公平锁*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
lock等方法:通过其内部静态类Sync实现的,Sync继承AQS
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
.
.
.
非公平锁(NonfairSync)的获取
1)、NonfairSync,ReentrantLock的内部类
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1)) { //CAS获取锁(修改锁状态成功则获取锁成功)
setExclusiveOwnerThread(Thread.currentThread()); //设置当前线程为拥有独占访问权限的线程
} else {
acquire(1); //以独占锁的方式去获取锁,具体方法在AbstractQueuedSynchronizer中
}
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires); //尝试获取非公平锁,上面acquire(1)中会用到这个,在Sync中
}
}
2)、acquire(int arg),AQS中的方法,以独占锁的方式去获取锁
public final void acquire(int arg) {
//尝试获取锁,获取成功则返回,不成功则添加到等待队列中,线程执行interrupt(),安全中断
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private static void selfInterrupt() {
Thread.currentThread().interrupt();//中断当前线程
}
3)、 nonfairTryAcquire(int acquires),Sync中方法,尝试获取不公平锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取锁的状态
if (c == 0) {//锁未被占用
if (compareAndSetState(0, acquires)) {//CAS获取锁
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;
}
4)、addWaiter(Node mode),AQS中的方法,通过给定模式为当前线程创建排队节点
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//为当前线程新建一个Node对象
Node pred = tail;//同步队列尾节点
if (pred != null) {//同步队列尾节点存在,同步队列不为空,将当前线程的Node对象加到同步队列后
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);//同步队列为空,则调用enq方法,新建同步队列,并将node加到队列中
return node;
}
5)、 enq(final Node node),AQS中的方法,将节点插入同步队列,同步队列为空则初始化
private Node enq(final Node node) {
//同步为空,初始化,再将node加入同步队列;同步队列不为空,直接将node加到队列中
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
6)、acquireQueued(final Node node, int arg),AQS中的方法,以独占且不可中断的方式获取同步状态
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {//想获取锁的线程在它前一节点为head时就进行自旋,检查前一节点是否释放锁;一单释放锁,当前节点就能拿到锁
final Node p = node.predecessor();//获取当前节点前一节点
if (p == head && tryAcquire(arg)) {//p节点是head节点,尝试性获取锁,获取锁过程只是一个判断,不会存在开销问题,这样既能提高性能,避免阻塞,又能执行抢占锁的操作。
setHead(node);//将当前节点设置为head节点
p.next = null; // help GC
failed = false;
return interrupted;
}
//判断p节点是否是同步队列的head节点,如果不是,则需要判断当前线程是否需要进入waiting状态;如果需要,则park()让当前线程进入waiting状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;//中断标识设置为true
}
} finally {
if (failed)
cancelAcquire(node);
}
}
7)、shouldParkAfterFailedAcquire(Node pred, Node node),AQS中的方法,获取当前节点前一节点的状态,然后对当前节点进行对应的操作
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
8)、parkAndCheckInterrupt(),AQS中的方法,pack使线程进入waiting状态,并返回中断标识,Thread.interrupted()之后会将线程中断标识重置为false
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
9)、cancelAcquire(Node node),AQS中方法,取消正在进行的获取尝试
private void cancelAcquire(Node node) {
if (node == null)//如果node不存在,直接返回
return;
node.thread = null;//
Node pred = node.prev;
while (pred.waitStatus > 0)//pred节点被取消
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED;//node状态
if (node == tail && compareAndSetTail(node, pred)) {//如果node节点是队列中的tail节点,则移除
compareAndSetNext(pred, predNext, null);
} else {
int ws;
//如果node不是tail节点,也不是head的后一节点,则将node的前一节点的waitStatus置为SIGNAL,并使node的前一节点指向node的后一节点
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);//node节点是head的后一节点,则唤醒它
}
node.next = node; // help GC
}
}
FairSync(公平锁)的获取
1)、FairSync ,ReentrantLock的内部类
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);//与非公平锁中获取独占锁一致
}
/**尝试获取锁*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取锁的状态
if (c == 0) {//没有线程获取锁
//判断当前线程是否是同步队列中的第一个线程,是则CAS操作获取锁,修改锁状态
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);//当前线程占据锁
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//重入判断,锁被占用情况下,判断是否是当前线程占用
int nextc = c + acquires;
if (nextc < 0)//超过最大锁计数
throw new Error("Maximum lock count exceeded");
setState(nextc);//设置锁的状态值
return true;
}
return false;
}
}
2)、acquire(int arg),AQS中的方法,以独占锁的方式去获取锁,与非公平锁的tryAcquire(int acquires)方法不同
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
锁的释放
1)、unlock(),ReentrantLock中方法,释放锁的入口
public void unlock() {
sync.release(1);
}
2)、 release(int arg),AQS中方法,线程在独占模式下释放共享资源
public final boolean release(int arg) {
if (tryRelease(arg)) {//尝试以独占模式释放
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒等待队列中的下一个线程
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
3)、tryRelease(int releases),Sync中方法,尝试以独占模式释放资源
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//锁的状态值-1
if (Thread.currentThread() != getExclusiveOwnerThread())//当前线程不是拥有独占访问权限的线程,抛出异常
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);//清空占据锁的线程信息
}
setState(c);//更新锁的状态值
return free;
}
4)、unparkSuccessor(Node node),AQS中方法,唤醒等待队列中下一个线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;//线程等待状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//如果队列中存在下一个线程,唤醒它
}
可中断的获取锁
1)、lockInterruptibly(),ReentrantLock中方法,可中断地获取锁入口
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
2)、acquireInterruptibly(int arg),AQS中方法,以独占模式获取,如果中断则中止
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//检查线程标志位,true表示线程中断,调用后会重置为false
throw new InterruptedException();
if (!tryAcquire(arg))//尝试获取锁
doAcquireInterruptibly(arg);//以独占可中断模式获取锁
}
3)、doAcquireInterruptibly(int arg),AQS中方法,以独占可中断模式获取锁
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);//为当前线程创建独占模式节点,添加到同步队列
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();//获取当前节点前一节点
if (p == head && tryAcquire(arg)) {
setHead(node);将当前节点设置设置为head节点
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
其他方法就不一一解析了,有兴趣的朋友可以自己去看看。
注意:看这部分源码的时候,发现:NonfairSync/FairSync继承Sync,Sync继承了AbstractQueuedSynchronizer,这里就有一个多重继承。为什么能够多重继承?NonfairSync、FairSync、Sync都是ReentrantLock的内部类,内部类可以继承一个与外部类无关的类,保证了内部类的独立性,所以这里才可以多重继承。
如果有写的不对的地方,请大家多多批评指正,非常感谢!