首先看看它的类图(拿来吧x黑马)
可以知道ReentrantLock里面实现了一个AQS,叫Sync,但是它是抽象的,具体的分为公平锁与非公平锁。
非公平锁
默认的构造方法就是非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
加锁流程
public void lock() {
sync.lock();
}
再点进去看, 发现是ReentrantLock里面继承了AQS的抽象的Sync。看它的子类NonfairSync
,可以看到它的加锁方法:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
首先是使用CAS对state
改变,它为0时无锁,为1是有锁。
成功,就设置独占锁为当前线程,加锁成功。
若失败,走到acquire(1)。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里呢,首先会让这个线程再次尝试获得锁,tryAcquire
。如果成功(比如锁的线程释放了),那就会跳出这个判断。
如果不成功,就把它放到等待队列中,也就是调用acquireQueued的方法。
先说说它的tryAcquire
,是尝试抢夺锁的,对应代码如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 当前没有锁独占
if (c == 0) {
// 尝试抢夺
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 可重入锁,如果独占锁的线程还是当前线程
else if (current == getExclusiveOwnerThread()) {
// 改state值
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
尝试抢夺失败后,调用acquireQueued
方法,在这里再次尝试抢夺锁。
不过在调用它之前,有一个addWaiter的方法,构造一个等待队列,有头、尾指针,还有一个虚结点Node(null)。以后添加元素,都往虚结点的后面加。
这里把Node加进去后,其实就是受NonfairSync管理的一个双向链表。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 这个循环会走多次
for (;;) {
final Node p = node.predecessor();
// 第一次,如果自己是在head后面(第二位)
// 会再次tryAcquire获得锁,成功就返回,失败就走下面
// 第二次,再次尝试获得锁,如果还是-1,就往下
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 接上第一次,将前驱node的waitStatus改为-1,返回false
// 接上第二次,已经改ws为-1了,然后走下面的方法
// 就被park了
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 被唤醒了
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
waitStatus表示它有职责唤醒它的后续结点,因此在双向队列中,虚结点和其后面的结点(除了最后一个,它为0)的ws都为-1,
解锁流程
public void unlock() {
sync.release(1);
}
尝试解锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
如果尝试解决成功,那就让它唤醒下一个线程,然后结束。
unparkSuccessor就是找到队列中离head最近一个Node,unpark恢复运行。
protected final boolean tryRelease(int releases) {
// 检测可重入的,-1,改变的只是锁计数
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 设置独占为null,解锁了
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
怎么尝试解锁 ?
tryRelease成功后,就把独占的owner设为null,并修改state变量,然后再走unparkSuccessor
这个进行unpark操作。
那这个线程unpark之后,就回到加锁时的acquireQueued
中,它被唤醒后,继续它的死循环,然后抢夺锁。
注意,虽然唤醒了链表中的下一个线程,但是它也有可能竞争失败的,因为它是非公平锁,有可能别的线程抢到了锁,这时它又要在循环中抢夺,失败的话再入链表。。。
可重入原理
其实就是修改state的值,加一或减一。
可以看看上面加解锁的nonfairTryAcquire
和tryRelease
。
不可打断原理与可打断原理
我认为是外部如果对线程进行unpark的话,只是改变它的标志,但是并不能真正地释放锁。它还是在AQS中,只有在获得锁后才知道自己打断了。
acquireQueued中,多次抢夺都抢不到锁,就会被打断
private final boolean parkAndCheckInterrupt() {
// 等待。如果打断标志为true,park会失效
LockSupport.park(this);
// interrupted会清除打断标志
return Thread.interrupted();
}
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())
// 只是改变标记,然后再走循环,还是会被park住,没有释放锁
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 如果打断状态为true
selfInterrupt();
}
static void selfInterrupt() {
// 重新产生一次中断
Thread.currentThread().interrupt();
}
公平锁原理
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 看看队列中有没有元素,如果没有
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;
}
它不会去抢夺锁,而且看看队列中是否有元素,如果没有,才去拿锁,不然的话在队列中乖乖排队。
条件变量实现原理
每个条件变量对应一个等待队列,实现类ConditionObject。
它是一个双向链表,有firstWaiter和lastWaiter。进入队列的状态会被设置Node.CONDITION
,值为-2。
移出队列后,进入等待线程,然后改状态。。。
都是链表的操作,学到了。