类图
概念
重入锁就是递归锁:当前线程获取到锁后 没有释放 还可以继续获取
当前线程有个计数器
private volatile int state;
加锁(抢占锁)
如果当前线程没有竞争到锁放到clh队列锁里
lock
-->sync.lock
-->tryAcquire(arg)
-->nonfairTryAcquire(acquires)(判断state为0否)
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
添加到node
先去尝试获取锁 如果没有获取成功 就在CLH队列中增加一个当前线程的节点,表示等待抢占
然后进入CLH队列的抢占模式 进入的时候也会执行一次获取锁的操作,如果还是获取不到
就调用LockSupport.park()将当前线程挂起。那么当前线程声明时候会被唤醒呢?当持有锁的那个线程调用unlock()的时候,会将CLH队列的头节点的下个节点上的线程唤醒,调用的是LockSupport.unpark()方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
没有抢到锁 通过parkAndCheckInterrupt() LockSupport.park(this)挂起
tryAcquire–>nonfairTryAcquire()
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;
}
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);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
公平锁
通过cas
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
非公平锁
FairSync相对来说就简单很多,只有重写的两个方法跟NonfairSync不同
final void lock() {
acquire(1);
}
if (!hasQueuedPredecessors() //就这里和非公平锁不一致 队列里面没有前置节点才CAS 有就直接从队列里面取
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;
}
释放锁
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) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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);
}
CLH队列锁
多个线程竞争锁 同一时刻只有一个线程拿到锁 其他线程都在队列里面排队 队列锁是用的链表结构
存放了上一个节点 下一个节点 当前线程
static final class Node {
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
}
本质
可重入锁的本质就是在当前线程加了一个计数器
所谓重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的
如果A线程的lock unLock不匹配 B线程一直拿不到锁
多个线程竞争锁时 没有竞争到锁的线程 封装成一个Node对象放入CLH锁队列里面
小结
现在都使用无锁化编程
因为有锁就有阻塞 阻塞就有上下文切换
cpu空转比上下文切换耗的性能少