ReentrantLock
构造
实现了Lock和serializable接口
ReentrantLock implements Lock, java.io.Serializable
成员变量
private final Sync sync;
abstract static class Sync;
static final class NonfairSync extends Sync;
static final class FairSync extends Sync;
提供的方法
public void lock();
public void lockInterruptibly();
public boolean tryLock();
public void unlock();
public boolean tryLock(long timeout, TimeUnit unit);
public Condition newCondition()
原理分析
//创建一个重入锁
ReentrantLock lock=new ReentrantLock();
//重入锁的构造函数可以看到,重入锁默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
可以看到可重入锁默认构建一个非公平锁,调用锁的lock方法,进入lock方法
final void lock() {
//cas设置把当前同步器的状态设置为1
if (compareAndSetState(0, 1))
//状态设置成功的话,当前同步器中的独占线程设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
//如果状态没有设置成功,进入方法
acquire(1);
}
进入compareAndSetState方法,cas设置状态
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
进入setExclusiveOwnerThread方法,设置AQS的独占锁
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
如果状态没有设置成功,需要进入acquire进行抢占
public final void acquire(int arg) {
//尝试获取锁
if (!tryAcquire(arg) &&
//没有获取的到,队列里面获取
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//自身中断
selfInterrupt();
}
进入tryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
进入nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取当前aqs状态
int c = getState();
if (c == 0) {
//如果状态设置成功,说明现在还没有线程获取锁,把当前线程设置为独占锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前的独占锁就是当前抢占锁的线程,aqs的当前状态+1,并且设置状态
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;
}
nonfairTryAcquire方法如果返回true的话说明获得了锁,返回false说明没有抢占到锁,没有抢到锁的话会进入addWaiter方法,抢到的话会返回
private Node addWaiter(Node mode) {
//把当前线程封装为一个node
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//把aqs中的尾节点赋值给pred
Node pred = tail;
//如果为节点不是空的话
if (pred != null) {
//把为节点赋值给当前节点的上一个节点
node.prev = pred;
//cas把aqs的为节点设置为当前节点
if (compareAndSetTail(pred, node)) {
//双向列表,把上一个节点指向的下一个节点设置为组装的node节点
pred.next = node;
return node;
}
}
//尾节点为空的话进入方法
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
//把尾节点赋值给t
Node t = tail;
//当前节点是空的话
if (t == null) { // Must initialize
//构建一个新的节点cas把头结点设置为新节点
if (compareAndSetHead(new Node()))
//把头结点赋值给tail(此时尾节点头节点都是新构建的节点)
tail = head;
//再次进入循环
} else {
//把尾节点赋值给当前节点的前节点
node.prev = t;
//cas把当前节点设置为尾节点(此时的尾节点是新构建的节点)
if (compareAndSetTail(t, node)) {
//把当前node节点赋值给新构建的节点的下一个节点(构建双向列表)
t.next = node;
return t;
}
}
}
}
当前线程组装node节点,并且在addWaiter构建一个双向队列,之后返回组装的节点,作为入参传递给acquireQueued方法
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)) {
//把当前节点设置为头结点,并且把当前节点的上个节点下个节点都设置为null
setHead(node);
p.next = null; // help GC
failed = false;
//返回
return interrupted;
}
//如果当前有线程获得锁,也就是当前线程没有抢到锁的话
if (shouldParkAfterFailedAcquire(p, node) &&
//挂起并检查当前线程是否被中断
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire判断在抢占锁失败后,是否该挂起
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//判断前节点是否是singnal状态
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//过滤掉canel状态的节点
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//把前节点设置为singnal状态
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
如果前节点是singal状态,当前节点应该被挂起,parkAndCheckInterrupt方法会挂起线程
private final boolean parkAndCheckInterrupt() {
//挂起线程
LockSupport.park(this);
//复位线程状态
return Thread.interrupted();
}
线程到这边会挂起阻塞,等待唤醒,加入获得锁的线程释放了锁,阻塞线程被唤醒的话,parkAndCheckInterrupt返回true,在acquireQueued上面方法中,会再次循环
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);
}
}
selfInterrupt()是AQS中实现,源码如下:
private static void selfInterrupt() {
Thread.currentThread().interrupt();
}
说明:selfInterrupt()的代码很简单,就是“当前线程”自己产生一个中断。但是,为什么需要这么做呢? 这必须结合acquireQueued()进行分析。如果在acquireQueued()中,当前线程被中断过,则执行selfInterrupt();否则不会执行。
在acquireQueued()中,即使是线程在阻塞状态被中断唤醒而获取到cpu执行权利;但是,如果该线程的前面还有其它等待锁的线程,根据公平性原则,该线程依然无法获取到锁。它会再次阻塞! 该线程再次阻塞,直到该线程被它的前面等待锁的线程锁唤醒;线程才会获取锁,然后“真正执行起来”! 也就是说,在该线程“成功获取锁并真正执行起来”之前,它的中断会被忽略并且中断标记会被清除! 因为在parkAndCheckInterrupt()中,我们线程的中断状态时调用了Thread.interrupted()。该函数不同于Thread的isInterrupted()函数,isInterrupted()仅仅返回中断状态,而interrupted()在返回当前中断状态之后,还会清除中断状态。 正因为之前的中断状态被清除了,所以这里需要调用selfInterrupt()重新产生一个中断!
小结:selfInterrupt()的作用就是当前线程自己产生一个中断。
总结
再回过头看看acquire()函数,它最终的目的是获取锁!
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
(01) 先是通过tryAcquire()尝试获取锁。获取成功的话,直接返回;尝试失败的话,再通过acquireQueued()获取锁。 (02) 尝试失败的情况下,会先通过addWaiter()来将“当前线程”加入到"CLH队列"末尾;然后调用acquireQueued(),在CLH队列中排序等待获取锁,在此过程中,线程处于休眠状态。直到获取锁了才返回。 如果在休眠等待过程中被中断过,则调用selfInterrupt()来自己产生一个中断。