ReentrantLock底层有AQS和CAS的原理,要先了解下
AQS
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。
如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
原理图
CAS
CAS(compare and swap),比较并交换。可以解决多线程并行情况下使用锁造成性能损耗的一种机制.CAS 操作包含三个操作数—内存位置(V)、预期原值(A)和新值(B)。
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。一个线程从主内存中得到num值,并对num进行操作,写入值的时候,线程会把第一次取到的num值和主内存中num值进行比较,如果相等,就会将改变后的num写入主内存,如果不相等,则一直循环对比,知道成功为止。
我的理解:往往我们操作一个参数的时候,比如说i=0,在进行i++的时候,是要先将i的值取出来,然后+1,最后才刷新会内存(这里会涉及到JAVA的内存模型),这其实就是三个指令,如果我现在只执行了第一条指令就cpu时间片到了,挂起来了,当再次获取到时间片的时候,这个时候i还是0吗,还是原来的0(ABA问题)吗???
所以说CAS就可以将我们三条指令变成一个原子性的操作,当然可以加入volatile关键字来确保顺序性和可见性
CAS问题:
-
ABA问题:加入版本号,可以利用AtomicStampReference的版本号 -
只能对一个参数进行原子性操作问题 -
自旋的时间问题
ReentrantLock
所有的方法我都是在JDK11上查看的,可能其他版本会有所不同
lock()方法,获取锁
就是执行sync.acquire(1)方法
acquire()方法
主要是执行tryAcquire(有公平锁和非公平锁锁的区别),addWaiter,acquireQueued这三个方法
//尝试获取锁
public final void acquire(int arg) {
//尝试获取锁
if (!tryAcquire(arg) &&
//addWaiter方法:如果获取锁不成功就将当前线程加入等待队列中,但此时线程还在运行
//acquireQueued方法: 继续尝试获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()方法,非公平锁
//非公平锁执行tryAcquire会进入下面这个方法
final boolean nonfairTryAcquire(int acquires) {
//拿到当前线程
final Thread current = Thread.currentThread();
int c = getState();
//state为0表示没有线程持有锁
//state大于0就代表有线程持有锁
//大于1表示加锁线程重入了
if (c == 0) {
//CAS比较并交换,设置state为1
if (compareAndSetState(0, acquires)) {
//设置加锁线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//判断加锁线程是不是当前线程,是就将state++
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(),此时是公平锁
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;
}
hasQueuedPredecessors()方法
public final boolean hasQueuedPredecessors() {
Node h, s;
//第一种情况:如果head为空,表示没有线程持有锁,就直接返回false,调用方会取反
//第二种情况: 如果head为空, 表示可能有线程持有锁,因为第一次获取锁不会创建head,但即使返回false也会去尝试获取锁,
//第三种情况:如果head不为空,表示可能有线程持有锁(即使线程释放锁,在没有其他线程获取锁的情况下,head还是指向原来的node节点,不会变为null)
if ((h = head) != null) {
//
if ((s = h.next) == null || s.waitStatus > 0) {
s = null; // traverse in case of concurrent cancellation
//从尾节点开始往前找,找第一个waitStatus<0(已经挂起的)线程
for (Node p = tail; p != h && p != null; p = p.prev) {
if (p.waitStatus <= 0)
s = p;
}
}
//判断节点是不是自己,如果不是就返回true,调用方会取反
if (s != null && s.thread != Thread.currentThread())
return true;
}
return false;
}
addWaiter()方法
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
//设置当前节点的前驱为曾经的尾节点,
//debug会发现方法还将oldTail和tail变得不一样了,也就导致第一次for循环下面的if语句不成立
node.setPrevRelaxed(oldTail);
//CAS比较并交换将,将当前tail设置为当前节点
if (compareAndSetTail(oldTail, node)) {
//将以前的尾指针的后继节点设置为当前节点
oldTail.next = node;
return node;
}
} else {
//当前线程未能加锁成功,且head未初始化,将new一个node节点赋值给head和tail
//第一个线程尝试加锁不会走到这,因为他直接加锁成功了
initializeSyncQueue();
}
}
}
acquireQueued()
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
//拿到当前节点的前驱节点
final Node p = node.predecessor();
//head:可以表示加锁的那个线程,也可以表示才释放锁的线程
//如果前驱节点等于head,那么可能已经释放锁了,执行tryAcquire方法去尝试获取锁
if (p == head && tryAcquire(arg)) {
//设置head为当前节点
setHead(node);
//这是原head节点已经处理完了,让他的引用结束
p.next = null; // help GC
return interrupted;
}
//第一次循环shouldParkAfterFailedAcquire方法将waitStatus状态设置为-1
//第二次循环shouldParkAfterFailedAcquire方法将返回true,然后将当前线程挂起
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
trylock(),尝试获取锁
就是执行nonfairTryAcquire()方法,前面也讲过了
unlock(), 释放锁
执行unlock()方法,会调用sync.release(1);方法
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;
}
tryRelease()方法
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//等于0表示都已经解锁完成了
if (c == 0) {
free = true;
//最后将加锁线程设置为null
setExclusiveOwnerThread(null);
}
setState(c);
//非公平锁的情况,到这就代表可能会被其他线程获取到锁了
return free;
}
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)
node.compareAndSetWaitStatus(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.
*/
//这里是唤醒head节点后面的节点
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点往前找第一个被挂起的线程
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
//唤醒s这个线程
LockSupport.unpark(s.thread);
}