文章目录
前言
锁是用来处理多线程访问资源的安全性控制。Java常见锁有Synchronized、JUC下的Lock。Synchronized为JDK提供的重量级锁,JUC Lock通过自旋+CAS实现的轻量级锁。
一、ReentrantLock -可重入锁
内部持有Sync,Sync继承自AbstractQueuedSynchronizer-AQS。而Sync分别有NonfairSync-非公平锁和FairSync-公平锁具体实现。公平锁和非公平锁的区别为获取锁的线程是否需要排队获取锁。
1、构造
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
public ReentrantLock() {
// 默认采用非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2、获取锁-Lock
2.1、lock
// Sync.lock
public void lock() {
sync.lock();
}
// 非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 非公平锁先尝试获取资源 CAS
if (compareAndSetState(0, 1))
// 获取到资源后 设置当前现场为锁独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 排队获取锁
acquire(1);
}
}
2.2、acquire
// 尝试获取锁,获取不到就加入队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
2.3、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) {
// 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;
}
2.4、addWaiter
// 添加到任务队列
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
// 任务链尾结点是否存在任务
if (pred != null) {
// 设置当前节点的前置节点为尾结点
node.prev = pred;
// CAS 设置任务尾结点为当前任务
if (compareAndSetTail(pred, node)) {
// 设置尾结点的下一个节点为当前节点
pred.next = node;
return node;
}
}
enq(node);
return node;
2.5、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);
}
}
2.6、shouldParkAfterFailedAcquire
// 判断当前线程是否要先暂停
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* 其他线程处于SINGAL状态,需要停止当前线程
*/
return true;
if (ws > 0) {
/*
* 其他任务处于CONDITION、PROPAGATE跳过
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 其他状态设置为SINGAL
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
2.7、parkAndCheckInterrupt
// 暂停当前线程 暂停后的线程什么时候启动?---见unLock分析
private final boolean parkAndCheckInterrupt() {
// 暂停当前线程 等待被unpark
LockSupport.park(this);
return Thread.interrupted();
}
3、释放锁-unLock
3.1、unLock
// 就硬调用持有的Sync的方法
public void unlock() {
sync.release(1);
}
3.2、release
// 释放资源 释放后唤醒后一个任务节点
public final boolean release(int arg) {
// 释放资源
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 唤醒下一个节点
unparkSuccessor(h);
return true;
}
return false;
}
3.3、release
// 释放资源
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;
// 设置锁线程为null
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
3.4、unparkSuccessor
// 唤醒后一个任务节点
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)
// 唤醒 后回到 acquireQueued绩效获取锁
LockSupport.unpark(s.thread);
}
二、总结
ReentrantLock由CAS获取资源+自旋(减少内核态和用户态之间切换的资源消耗)+ UnSafe.Park 来实现