锁类继承层次
锁工具类LockSupport
public class LockSupport {
private static final Unsafe UNSAFE;
public static void park() {
UNSAFE.park(false, 0L);
}
public static void unpark(Thread var0) {
if (var0 != null) {
UNSAFE.unpark(var0);
}
}
}
线程中调用park()会阻塞当前线程,在其它线程中调用unpark(thread),可精准唤醒指定的线程
参数作用
AbstractOwnableSynchronizer
Thread exclusiveOwnerThread : 表示当前持有锁的线程
AbstractQueuedSynchronizer
volatile int state :当前锁的状态,cas保证线程安全
transient volatile Node head:阻塞队列头
transient volatile Node tail:阻塞队列尾
Node addWaiter(Node mode):将当前线程构建成node加入阻塞队列
boolean acquireQueued(final Node node, int arg):实现线程阻塞逻辑调用
boolean parkAndCheckInterrupt():线程的自我阻塞
boolean release(int arg) :解锁
NonFairSync&FairSync
void acquire(int arg):获取锁
final boolean tryAcquire(int acquires):尝试获取锁
boolean tryRelease(int releases) :尝试解锁
加锁流程图
acquire(int arg) 与 tryAcquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当 acquireQueued(…)返回 true 时,会调用 selfInterrupt(),自己给自己发送中断信号,也就是自
己把自己的中断标志位设为true。之所以要这么做,是因为自己在阻塞期间,收到其他线程中断信号没
有及时响应,现在要进行补偿。这样一来,如果该线程在lock代码块内部有调用sleep()之类的阻塞方
法,就可以抛出异常,响应该中断信号。
tryAcquire(int arg)
非公平锁实现
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;
}
公平锁实现
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())队列中是否有任务线程,没有才会去竞争锁状态(state)。而非公平锁则是直接去竞争锁状态(state)
阻塞逻辑acquireQueued(final Node node, int arg)
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;
}
//没有则进入线程阻塞parkAndCheckInterrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
进入队列中,都是公平锁了。会进行如下操作
1、判断是不是头节点,是的话回去尝试竞争锁状态
2、竞争到锁以后直接返回
3、没有则进入线程阻塞parkAndCheckInterrupt()
4、阻塞线程唤醒后,重复1234操作
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
解锁流程图
release(int arg)
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(int releases)
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;
}
修改状态时没有用到cas,因为此时处理是单线程的。状态等于0时才清空持锁线程,为了处理锁重入的情况。
unparkSuccessor(Node node)
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);
}
可中断锁的实现lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//中断标识检测
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//关键点
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
主要关键点在doAcquireInterruptibly方法中线程阻塞后判断中断标识,如果标识为true,则抛出InterruptedException。以及acquireInterruptibly方法中获取锁前的中断标识检测。
读写锁ReentrantReadWriteLock
实现原理,重写并拆分了AbstractQueuedSynchronizer中的state状态,高16位存储读锁的状态,低16位存储写锁的状态。
对于写锁来说,只需要判断state>0则说明有线程占有锁,就需要进入阻塞队列,而对于读锁来说,先判断state,等于0说明无线程占用,>0再判断是否写线程占用(低16位),写占用则进入阻塞队列,读占用则获取到锁。
写线程占有较高优先级,因为不这样做的话,可能多个读线程一直执行,导致写线程饥饿。
Condition
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = Lock.newCondition();
notEmpty.await();//阻塞
notEmpty.signal();//唤醒
Condition实现原理
所有的condition都是借助ConditionObject对象来实现。对象里面维护一个双向阻塞线程链表,通过这个链表来进行统一的唤醒操作。
StampedLock锁
jdk1.8以后提供,相比较于ReentrantWriteLock。读写默认是不互斥的。只有当读写冲突时,才会升级为互斥。数据读出来后会有个版本号,再使用之前会用版本号校验是否被改过,若被改过才会去竞争锁,重新读数据。
StampedLock stampLock = new StampedLock();
long stamp=stampLock.writeLock();//上写锁
stampLock.unWriteLock(stamp);//解锁
long rStamp=stampLock.tryOptimisticRead();//乐观读
if(!stampLock.validate(rStamp)){
stampLock.readLock();//加读锁
stampLock.unReadLock(rStamp);//解读锁
}