一、简介
1)什么是ReentrantLock?
ReentrantLock是一个可重入锁、可打断锁、公平锁、非公平锁,ReentrantLock不同于synchronized的是,ReentrantLock释放锁是由我们决定的,而synchronized释放锁是不受我们控制的。
2)为什么有了synchronized之后还要实现ReentrantLock?
- synchronized是jvm层面的锁而ReentrantLock是一个类。
- synchronized释放锁不受控制,ReentrantLock可以有我们控制锁释放,能更合理的防止高并发出现的问题。
- synchronized因为在进行等待时是没有超时设置的容易造成死锁问题,而ReentrantLock是可打断的锁并且可以设置等待超时时间这样子可以避免死锁问题。
3)ReentrantLock的继承关系
二、构造方法
/**
* 无参构造方法,ReentrantLock默认创建的是非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 根据传入的布尔值确定创建公平锁还是非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
三、核心内部类
1)sync类
公平锁和非公平锁的父类,实现主要的逻辑,剩下的lock()和tryAcquire()由子类实现
/**
* 此锁的同步控制基础。子类分别公平和非公平版本的锁。使用 AQS 状态表示锁上的保留数。
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
/**
* 非公平锁的尝试获取锁
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前锁状态
int c = getState();
// 如果c==0说明此时的锁是没有被占有的
if (c == 0) {
// 使用CAS去修改锁的状态值
if (compareAndSetState(0, acquires)) {
// 把锁的持有者修改为当前线程
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;
}
/**
* 尝试释放锁
*/
protected final boolean tryRelease(int releases) {
// 获取锁的状态值
int c = getState() - releases;
// 如果当前线程不是锁的持有者则无法释放锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 确保重入的线程都释放了锁才算释放成功
if (c == 0) {
free = true;
// 将锁持有者设置为空
setExclusiveOwnerThread(null);
}
// 修改锁的状态值
setState(c);
return free;
}
/**
* 判断当前线程是否是锁的持有者
*/
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 创建新的线程等待池,用于存储因不同原因阻塞的线程
*/
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
/**
* 获取锁的持有者
*/
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/**
* 返回锁的状态值
*/
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/**
* 判断锁是否被占用
*/
final boolean isLocked() {
return getState() != 0;
}
/**
* 自定义序列化流程
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
2)FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* 调用AQS的acquire方法
*/
final void lock() {
acquire(1);
}
/**
* 公平锁的尝试获取锁的方法
*/
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取锁的状态
int c = getState();
// 如果c==0说明锁没有被占用
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;
}
}
3)NonFairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
四、加锁流程
1)公平锁
reentrantLock.lock() -> fairSync.lock() -> acquire() -> tryAcquire()
/**
* 开始加锁流程,根据创建的锁对象调用lock方法
*/
public void lock() {
sync.lock();
}
/**
* 公平锁的加锁方法
*/
final void lock() {
acquire(1);
}
/**
* 先尝试获取锁,如果成功就直接返回,如果失败则将线程加入等待队列
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* 公平锁的尝试获取锁的方法
*/
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取锁的状态
int c = getState();
// 如果c==0说明锁没有被占用
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;
}
/**
* 加锁失败则将线程存入等待队列
*/
final boolean acquireQueued(final Node node, int arg) {
// 设置表示符
boolean failed = true;
try {
// 打断标记
boolean interrupted = false;
// 在死循环中不断获取锁,如果失败就直接挂起
for (;;) {
// 获取传入节点的前驱节点
final Node p = node.predecessor();
// 如果前驱节点是head就再次尝试获取锁
// 因为这是公平锁,所以先进队列的先获取锁
if (p == head && tryAcquire(arg)) {
// 如果node获取锁成功则将node设置为哨兵节点
setHead(node);
// 释放上一个哨兵节点
p.next = null; // help GC
// 设置标识
failed = false;
return interrupted;
}
// 先判断当前节点的前驱节点是否能唤醒当前节点,如果能就直接挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 线程被挂起后代码就会卡在这不执行,直到前驱节点唤醒当前线程
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2)非公平锁
reentrantLock.lock() -> nonFairSync.lock() -> acquire() -> tryAcquire()
/**
* 开始加锁流程,根据创建的锁对象调用lock方法
*/
public void lock() {
sync.lock();
}
/**
* NonFairLock的lock方法
*/
final void lock() {
// 尝试直接获取锁,获取成功则直接修改线程持有者
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 尝试获取锁
acquire(1);
}
/**
* 非公平锁不需要检查等待队列,只需要唤醒全部等待线程
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* 调用非公平锁的方法
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* 非公平锁的尝试获取锁
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前锁状态
int c = getState();
// 如果c==0说明此时的锁是没有被占有的
if (c == 0) {
// 使用CAS去修改锁的状态值
if (compareAndSetState(0, acquires)) {
// 把锁的持有者修改为当前线程
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;
}
五、释放锁流程
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;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* 唤醒在等待队列中的线程。
*
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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)某个线程获取锁失败的后续流程是什么呢?
进入线程等待池中等待,仍然保留获取锁的可能,获取锁流程仍在继续。
2)既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?
是CLH变体的FIFO双端队列。
3)处于排队等候机制中的线程,什么时候可以有机会获取锁呢?
当某个线程释放锁时会去查看队列中有无线程在等待,有就会先释放head的下一节点的线程。
4)如果处于排队等候机制中的线程一直无法获取锁,需要一直等待么?还是有别的策略来解决这一问题?
不会一直等待,线程所在节点的状态会变成取消状态,取消状态的节点会从队列中释放
5)Lock方法通过Acquire方法进行加锁,但是具体是如何加锁的呢?
AQS的Acquire会调用tryAcquire方法,tryAcquire由各个自定义同步器实现,通过tryAcquire完成加锁过程。