一、使用方式
//使用方式
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
lock.lock();
try {
System.out.println("当前线程名称名称位第一层:" + Thread.currentThread().getName());
lock.lock();
try {
System.out.println("当前线程名称名称位第二层:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"A").start();
new Thread(()->{
lock.lock();
try {
System.out.println("当前线程名称名称位:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"B").start();
上面使用ReentrantLock方式,实现的加锁程序,其中lock()和unlock()是一一对应的,如果缺少一个都会导致程序运行异常。
二、构造方法
//默认构造函数---》默认使用的是非公平所的方式
public ReentrantLock() {
sync = new NonfairSync();
}
//带参数的构造函数=====》根据传入的参数true/false,来判断走公平或非公平的方式
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock的加锁方式分为:公平锁和非公平锁两种模式,默认为非公平的加锁方式,后面具体分析
三、lock()具体分析
1、lock解析
//公平锁的lock方法
final void lock() {
acquire(1);
}
//非公平锁的lock方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
公平锁直接调用acquire(1)方法进入排队状态,非公平锁是先获取锁,如果获取失败,则进入队列排队。下面先看一下compareAndSetState(0, 1)这个方法。
2、compareAndSetState(0, 1)
//AbstractQueuedSynchronizer类中的compareAndSetState方法
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
/**
* The synchronization state. 同步状态标识
*/
private volatile int state;
这边要引入一个变量state,如果state == 0 的时候,表示没有加锁,如果state>0的时候,表示已经加锁。无法获取锁,compareAndSetState使用的底层CAS的方式进行操作的,比较state变量的值,如果是0直接变成1,加锁完成,然后通过setExclusiveOwnerThread(Thread.currentThread())设置当前线程占有锁,否则,加锁失败,进入队列排队
3、acquire(1)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire方法中,没有具体的落地实现,调用了其他方法,下面我们逐个解析这些方法。
1)tryAcquire(arg)
//AbstractQueuedSynchronizer 类中的tryAcquire只有抛出异常的操作,但是在他的实现类中,可以找
//到对应实现操作
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
//实现类ReentrantLock 中FairSync类的tryAcquire方法(公平锁)
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//当前线程的状态,就是前面说的state状态
int c = getState();
//如果等于0 ,再一次尝试占有锁,如果获取成功,则直接退出排队
if (c == 0) {
//hasQueuedPredecessors()表示没有排队的队列
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;
}
//实现类ReentrantLock 中FairSync类的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()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
tryAcquire方法中,首先获取state的值等于是0,表示无锁的状态,再次判断hasQueuedPredecessors()是否有配对的队列,这边对于公平锁和非公平锁是有区别的,非公平锁,不调用此方法,没有排队的队列,进入compareAndSetState的方式占有锁;如果state的值大于0,并且正要加锁的线程,就是已经加锁的线程,表示可以重复占有锁,叫可重入锁。然后把state的值+1。
2)创建队列addWaiter(Node.EXCLUSIVE)
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//把尾节点赋值给pred节点,第一次创建时队列的时候tail尾节点是空的,就会走到enq(node)这个方法里面创建队列
Node pred = tail;
//tail节点不为空,表示当前队列已经存在,下面是构建双向队列的过程,跟enq里面的相似
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
//尾节点赋值给t
Node t = tail;
//第一次创建队列的时候,尾节点tail肯定是为空的,所以要初始化
if (t == null) { // Must initialize
//设置头节点的值为空节点,
if (compareAndSetHead(new Node()))
//把尾节点指向头节点 == 空节点不是null
tail = head;
} else {
//队列存在的情况,把当前节点的前置节点置为t节点
node.prev = t;
//把当前节点置为尾节点
if (compareAndSetTail(t, node)) {
//双向队列,把t的下一个节点置为当前节点
t.next = node;
return t;
}
}
}
}
把尾节点赋值给pred节点,第一次创建时队列的时候tail尾节点是空的,就会走到enq(node)这个方法里面创建队列,在看下enq这个方法,它是for的死循环方法,这边要注意,构建的双向队列之前,head == tail == null ,在构建队列的时候,第一个节点为空,没有任何数据,在第一个数据放入之后,队列应该是下面这个样子的
3)设置当前线程阻塞acquireQueued方法
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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节点的下一个节点之空
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//这边的waitStatus默认初始值为空
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // static final int SIGNAL = -1;
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
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 {
/*
* 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.
*/
//把waitStatus设置成为 -1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
LockSupport.park(this);
return Thread.interrupted();
}
这边的主要处理逻辑是再次占有线程的和 LockSupport.park(this)来阻塞线程。
至此,ReentrantLock的加锁流程源代码已经梳理完成,其中值得注意的是AbstractQueuedSynchronizer(AQS),不管是公平锁,还是非公平锁,都是在继承AQS类的基础上实现的。AQS的主要思想是state+双向队列控制的。
四、unlock()方法实现
1、unlock方法
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;
}
如果释放成功,看一下头节点的数据,上面有说过,头节点是一个没有数据的空的node,它的waitStatus状态在shouldParkAfterFailedAcquire这个方法中,已经置为了-1,所以这边会继续往下走.
2、tryRelease(arg)方法
//这边就是一个抛出异常的操作,具体实现在ReentrantLock里面
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
//ReentrantLock的tryRelease实现
protected final boolean tryRelease(int releases) {
//获取当前state的值 ,减去传进来的值,一般值是1
int c = getState() - releases;
//当前线程不是占有锁的先,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果c== 0 ,则设置排他锁为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
这个方法主要是设置state的值
3、unparkSuccessor方法
private void unparkSuccessor(Node node) {
//这边的node是头节点,也就是那个空节点,它的状态在目前是-1,
int ws = node.waitStatus;
if (ws < 0)
//把waitStatus的状态置为0;
compareAndSetWaitStatus(node, ws, 0);
//s是需要唤醒的线程,也就是头节点的下一个节点
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);
}
这边主要是设置节点waitStatus的值,使其恢复为0,并唤醒当前线程。