1、初次相识
在ReentrantLock锁代码中,你发现,锁的动作是由Sync的实例来处理,而Sync是继承了AbstractQueuedSynchronizer;而在ReentrantReadWriteLock锁中也会由这样的发现;没错,AbstractQueuedSynchronizer是锁实现的基石,实现了资源相关线程出队入队、唤醒、暂停等逻辑;通过这些逻辑,你可以实现可重入锁,共享锁,独占锁,条件锁,公平锁,非公平锁(条件锁,不是一种锁,是一种使用方法)。
2、原理
锁其实是对一些资源的保护,拥有这些资源,可以继续执行,不持有资源执行权限则等待资源被释放后,再次竞争资源的执行权限;我在本文中也习惯,把线程可以继续执行,看成是持有资源执行权限,若被挂起,则表明未持有资源,且资源被其它线程持有,若果被唤醒,则需要继续竞争资源的执行权限
其内部实现有一些重要的类,我们先讲讲,然后再了解具体实现
2.1 Unsafe类
Unsafe类是在sun.misc包下,不属于Java标准,android中也不是标准支持,可以通过使用java库的方式+反射调用,另外可以用LockSupport类+Atomic原子类操作来代替Unsafe类的功能,其时CAS操作的基石;Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,一旦能够直接操作内存,因此操作时,需要对象地址,对象成员变量相对地址偏移量,旧值,要修改后的值;
两个方面的用途
- 线程的停止、唤醒
- 基础数据、对象的写,具有原子性
其使用如下:
这时AtomicInteger的代码:首先静态获取变量相对类的偏移量,在对象处理时,使用当前对象地址+偏移量,推算出当前变量地址
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long VALUE;
static {
try {
VALUE = U.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private volatile int value;
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
}
代码解读:
- VALUE 为value变量在AtmicInteger实例地址的相对偏移量,这个相对偏移量对于类,在类加载后就是一定的
- U提供了设置方法,但是为了保证读写操作的原子性,我们一般选择使用volatile+和旧值比对然后替换的方法来达到原子性
- 和旧值比对,如果一致,替换才能成功,否则不能成功
原子性的原理:
- 读的结果为最新
- 写的时候,旧值符合预期才能成功,如果变动了则旧值旧变化了,也就不能写成功了 存在问题ABA问题,也就是旧值在一个线程变为A,让后在另外一个线程变为B,某个线程为A;你当时获取为A,那么也是可以修改为你想要的了;这个问题是添加时间戳来记录旧值并比对
2.2 静态内部Node类
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() {}
Node(Node nextWaiter) {
this.nextWaiter = nextWaiter;
U.putObject(this, THREAD, Thread.currentThread());
}
Node(int waitStatus) {
U.putInt(this, WAITSTATUS, waitStatus);
U.putObject(this, THREAD, Thread.currentThread());
}
final boolean compareAndSetWaitStatus(int expect, int update) {
return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
}
final boolean compareAndSetNext(Node expect, Node update) {
return U.compareAndSwapObject(this, NEXT, expect, update);
}
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long NEXT;
static final long PREV;
private static final long THREAD;
private static final long WAITSTATUS;
static {
try {
NEXT = U.objectFieldOffset
(Node.class.getDeclaredField("next"));
PREV = U.objectFieldOffset
(Node.class.getDeclaredField("prev"));
THREAD = U.objectFieldOffset
(Node.class.getDeclaredField("thread"));
WAITSTATUS = U.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
}
主要内容有:
- 实现了next、prev、thread、waitStatus四个内部变量的CAS操作
- 双向链表结构的节点
- waitStatus变量标志线程thread的状态
2.3 条件内部类ConditionObject
条件锁的队头队尾,也是先进先出队列,并且其利用nextWaiter指向下一个条件节点,也就是单向链表
private transient Node firstWaiter;
private transient Node lastWaiter;
2.3.1 条件锁等待流程
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
- addConditionWaiter方法,进行加入条件队列
- fullyRelease方法:由于当前线程持有资源,才可以使用条件等待,所以需要先释放资源,让其它线程唤醒执行,才可以使用资源
- isOnSyncQueue方法,查看是否在锁的等待队列(外部类的队列,包含独占锁、共享锁的对象的那个),不在会被挂起;
- checkInterruptWhileWaiting方法,如果处于打断状态,则有可能把节点加入锁等待队列
- acquireQueued:尝试获取资源,若未获取,则挂起线程,否则,继续执行
- unlinkCancelledWaiters方法,条件队列重新检查,去除无效节点,被取消的线程节点
- reportInterruptAfterWait 方法,设置线程打断
2.3.2 条件锁唤醒流程
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
- isHeldExclusively方法直接抛出异常,需要自定义实现
- doSignalAll方法释放每一个节点
doSignalAll方法
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
其具体释放动作由transferForSignal实现
transferForSignal方法
final boolean transferForSignal(Node node) {
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
- 假设当前状态为条件状态,重置为运行状态,如果失败,则false,也即是进行唤醒下一个,尝试去获取资源:也就是这是状态已经被改变了
- 如果状态修改为运行状态,节点加入到锁等待队列;再次判断状态,如果已经取消,或者重置状态为等待唤醒失败,则唤醒线程;说明线程要么取消,要么状态已经改变,CAS置换为等待唤醒状态失败,则当前线程不能被唤醒(详细看下方独占锁释放时的判断),所以需要在这里释放
小结
- 条件锁,有专门的队列,存放条件等待线程节点;单向列表,使用nextWaiter指向下一个
- isHeldExclusively方法需要实现,表示是否为当前线程持有锁
- 条件锁释放后,条件等待队列中节点如果不是条件状态,说明已经被改变,也就是被唤醒
- 如果仍是condition状态,则加入锁等待队列,并重新检查状态,如果取消或者CAS重置状态失败,则唤醒线程
2.4 CAS操作内部变量
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long STATE;
private static final long HEAD;
private static final long TAIL;
static {
try {
STATE = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
HEAD = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
TAIL = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
Class<?> ensureLoaded = LockSupport.class;
}
原子操作的对象:状态state,队列头head,队列尾tail 内部变量state实现了原子非原子操作
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return U.compareAndSwapInt(this, STATE, expect, update);
}
内部变量head,也是实现了非原子操作
private transient volatile Node head;
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
private final void initializeSyncQueue() {
Node h;
if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
tail = h;
}
内部变量tail,也是实现了非原子操作
private transient volatile Node tail;
private final void initializeSyncQueue() {
Node h;
if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
tail = h;
}
private final boolean compareAndSetTail(Node expect, Node update) {
return U.compareAndSwapObject(this, TAIL, expect, update);
}
同样在内不对于head,tail变量还有直接使用的,也是不满足原子操作的,那么为何最后又能达到同步的效果呢,只能看具体代码了
2.5 独占锁实现细节
ReentrantLock是独占锁,而且内部可以是公平锁,非公平锁; 公平锁:
final void lock() {
acquire(1);
}
非公平锁:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
由此可见:
- 当state为0时,资源处于空闲状态, 其它线程可以获取资源的执行权限
- 当state为1时,资源已经被线程持有,其它线程再使用资源时需要排队等待资源被释放
- 所谓非公平锁,就是如果资源是未被持有,那么正在请求获取锁的线程,比等待队列中的锁优先
- 获取资源执行权限,调用同步器的acquire方法;结果可能未持有资源,线程则被挂起
2.5.1 上锁流程
acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
涉及简单方法分析:
- tryAcquire方法直接抛出异常,也即是自定义独占锁必须实现这个方法;
- selfInterrupt 获取锁的线程进行中断操作,这个并不一定导致线程停止
addWaiter方法
整体来说,就是加入一个节点到队列尾部;如果未初始化队列,则进行初始化(延时策略)
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
U.putObject(node, Node.PREV, oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue();
}
}
}
- 首先生成一个Node节点,这个节点nextWaiter为空(Node.EXCLUSIVE为空对象);独占锁的nextWaiter为空
- for循环自旋
- 如果队列未进行初始化,则initializeSyncQueue进行初始化,如果不成功,继续此步骤直至成功
- 加入队列尾部
acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
- for循环自旋;如果其是头节点下的第一个节点,如果尝试获取资源成功,则进行设置为队列头,释放之前头节点,并返回false;返回false,则意味线程跳出自旋,可以继续执行
- 如果不是锁等待队列的第二个,则执行shouldParkAfterFailedAcquire方法,如果为true,继续执行parkAndCheckInterrupt方法
- shouldParkAfterFailedAcquire方法执行后,返回false会去掉取消的节点,之后如果未有状态变化(比如外部取消线程,打断等操作),则会返回true,可以详细看下面方法源码
shouldParkAfterFailedAcquire方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
- 如果node 前一个节点pred节点已经是等待唤醒状态,则返回true,表示当线线程应该被暂停
- 如果node前一个节点状态大于0,暂时好像只有取消状态的,则找到一个状态小于等于0的,并是node为其后继节点,则寻找过程中的节点都会被移除队列,返回false
- 如果node前一个节点已经是小于等于0了,这时把前一个几点的状态置为等待唤醒-1,返回false
parkAndCheckInterrupt方法
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
- 暂停正在执行的线程,并返回打断状态
2.5.2 释放锁流程
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需要自定义实现,否则直接抛出异常
- 如果头节点不是正在运行状态,则解锁头节点线程,释放锁成功
- 否则不需要释放锁,或者释放失败(按照正常,在锁等待队列中,独占锁/条件锁,都会为signal状态,为共享锁为signal或者PROPAGATE状态)
unparkSuccessor方法
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
node.compareAndSetWaitStatus(ws, 0);
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)
LockSupport.unpark(s.thread);
}
- CAS操作,释放头节点状态设置为0
- 从头结点后寻找一个节点不为空,且节点为0的节点,释放此节点的线程(上述获取资源时,进行自旋,去除头结点后的取消的节点后的第一个节点才可以获取资源)
2.5.3 独占锁原理小结
- nextWaiter为空
- 排队等锁的队列,头优先获取资源(对于非公平锁,新获取未排队的线程也会获取锁);尝试获取资源的线程排队到队尾
- 获取资源失败的线程,被挂起;持有线程执行完毕,则头节点的下一个节点恢复执行,尝试获取资源(非公平锁,会和新获取锁未排队进来的线程争夺锁)成功后继续执行其任务,失败线程挂起,并置为等待唤醒状态
- 自定义独占锁需要实现tryRelease、tryAcquire方法
- 可重入锁,即当前线程再次获取资源,状态+1,释放资源状态-1,如果是0,则是当前线程完全释放了资源,其它排队线程可以获取资源了;代码如下:非公平锁的代码
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 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;
}
2.6 共享锁实现细节
参照ReentrantReadWriteLock的读锁为共享锁,写锁是独占锁
获取锁
public void lock() {
sync.acquireShared(1);
}
释放锁
public void unlock() {
sync.releaseShared(1);
}
2.6.1 获取锁流程
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
- tryAcquireShared 方法直接抛出异常,需要自定义实现
- tryAcquireShared 方法返回大于等于0时,则继续进行;小于0则获取锁失败
doAcquireShared方法
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
- 新建一个节点,节点的nextWaiter值为Node.SHARED,并加入队尾
- 如果其前驱节点,不是头节点,则暂停当前线程(方法分析,见独享锁)
- 如果前驱节点是头节点:尝试获取锁失败,也会暂停线程,如果成功则会执行方法setHeadAndPropagate,并返回true
setHeadAndPropagate方法
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
- 设置节点为HEAD节点
- 满足条件的话,唤醒下一个节点线程,满足下列之一:1)之前头节点为空,2)之前的线程状态小于0,3)现在的head节点为null或者节点相关线程状态小于0;并且需要满足node节点下一个节点不为空或者是共享节点
doReleaseShared方法
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}
自旋释放线程
- 头节点为空,或者与尾节点相同,则当前没有需要唤醒的线程,直接退出
- 如果头节点状态为待唤醒状态,则置换状态,唤醒线程(之后会接着尝试获取共享资源,接着释放后续共享资源节点。。。),结束自旋
- 如果头节点的状态正在运行,则CAS置换为-3失败,则重复流程
- CAS置换状态为-3,则结束
2.6.2 释放锁流程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
- tryReleaseShared直接抛出异常,需要自定义实现
- tryReleaseShared为true,则进行释放共享资源,并返回释放成功
2.6.3 共享锁原理小结
- nextWaiter为Node.SHARED
- 排队等锁的队列,头优先获取锁(对于非公平锁,新获取未排队的线程也会获取锁);尝试获取锁的线程排队到队尾
- 共享节点获取资源成功,依次唤醒相邻的共享资源节点;如果被唤醒的相邻节点再次执行tryAcquireShared不能获取执行权限,则再次被挂起
- 自定义共享锁需要实现tryReleaseShared、tryAcquireShared方法
3、同步器原理实现总结
- 实现独占锁,需要实现方法tryRelease、tryAcquire,isHeldExclusively方法
- 实现共享锁时,需要实现,tryReleaseShared、tryAcquireShared,isHeldExclusively方法;
- tryAcquire,tryAcquireShared结果返回true时,表示持有锁,则继续执行
- 使用条件锁时,条件队列内部竞争后,还会和锁队列竞争
- 锁通过state来标志是否需要等待,0无需等待,1表示锁被持有,> 1表示锁被持有且是可重入锁;
- 线程持有锁时,使用setExclusiveOwnerThread设置持有线程,使用getExclusiveOwnerThread获取持有线程
4、锁的实现
4.1 ReentrantLock锁
4.1.1 静态内部抽象类类Sync
并不是每个方法都进行分析,详情如下
- 重点方法nonfairTryAcquire,tryRelease和lock抽象方法
- isHeldExclusively方法实现,判断持有锁线程是否当前线程
- newCondition方法利用内部类,生成条件锁对象
- getOwner方法,获取持有锁线程对象
- getHoldCount方法获取当前线程持有锁重入次数
- isLocked方法判断已有线程持有锁
- readObject方法,序列化操作
一些特殊变量
private transient Thread firstReader;
private transient int firstReaderHoldCount;
共享锁第一个线程对象,和第一个线程对象重入次数
private transient ThreadLocalHoldCounter readHolds;
ThreadLoack对象,key为线程,value为HoldCounter对象(包含重入次数,线程tid)
private transient HoldCounter cachedHoldCounter;
缓存对象,上次共享锁尝试获取时,线程信息
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;
}
- 如果当前未有线程持有资源(锁等待队列为空,或者持有资源线程刚释放锁),CAS操作设置状态,如果成功,则设置当前线程为持有资源线程, 返回true;不公平有这个方面的原因,尝试获取资源的新线程,直接和锁等待队列表头竞争锁,失败后才放在锁等待队列队尾,也即是有可能比它先等锁的线程先执行
- 当前线程即为持有资源线程,状态+1,返回true;可重入性
- 其它情况,均未获取资源,进入锁等待队列
tryRelease方法
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;
}
- 首先判断当前线程 == 持有资源的线程,否则异常
- 如果状态减1,之后为0,则设置持有线程为null,返回true,表示资源可以被锁等待队列节点竞争
- 如果不为0,返回false,这时当前锁为可重入锁,线程持有资源的次数未进行完全释放,不需要去唤醒锁队列的线程
4.1.2 公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
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;
}
}
- 获取锁时,直接调用acquire获取锁,acquire是共享锁获取的直接入口,也说明,默认实现也就是公平锁
- hasQueuedPredecessors方法:true表示:锁队列头!=尾,头的下个节点为空或者头的下个节点线程!=当前线程
- 如果队列为空;进行原子改变状态操作,并设置持有锁线程,返回true;
- 如果当前线程就是持有资源的线程,则,状态+1,返回true;可重入性
- 其它情况均不能直接获取资源,
4.1.3 非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
- 尝试获取锁采用非公平获取方式nonfairTryAcquire方法,见4.1.1 Sync对象分析
- 尝试获取锁时,直接CAS操作当前状态,尝试从旧状态0设置为1,如果成功,则直接持有锁
4.2 ReentrantReadWriteLock读写锁
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
读锁对象、写锁对象都是静态内部抽象类Sync对象来代理实现的
4.2.1 静态内部抽象类Sync
读写锁数量
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
- 数据32位,读锁高位16位,写锁低位16位
- 读锁个数增加1个,则数据增加SHARED_UNIT,写锁个数增加1则数据就是+1
抽象方法
abstract boolean readerShouldBlock();
abstract boolean writerShouldBlock();
读写锁阻塞策略
tryAcquire方法
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
按照下面流程进行处理
- 资源已经被线程持有,独占锁个数为0或者当前线程不为持有线程,尝试获取资源失败
- 资源已经被线程持有,独占锁个数超标,抛异常
- 资源已经被线程持有,则尝试获取锁成功,state+1
- 资源未被持有,如果写被阻塞或者状态CAS操作失败,尝试获取锁失败
- 否则,直接获取锁成功,并设置当前线程为执行线程
tryRelease方法
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
- 检查释放锁线程是否为当前线程,不是,则抛出异常
- 如果当前state为0,则设置执行线程为空
- 设置当前状态,以state == 0 为返回结果
tryAcquireShared方法
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
代码执行流程如下:
- 如果独占锁线程个数不为0且当前线程不为执行线程,则返回-1
- 如果共享锁不被阻塞且共享锁个数没有达到最大,锁状态CAS操作成功,返回1;其中计算线程重入次数并记录,如果第一个共享锁线程,则使用firstReader,firstReaderHoldCount,记录线程对象,个数;否则使用readHolds记录线程tid和重入次数信息,重置缓存信息cachedHoldCounter
- 返回 fullTryAcquireShared方法执行结果
fullTryAcquireShared方法
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
if (firstReader == current) {
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh;
}
return 1;
}
}
}
for循环进行自旋,流程如下
- 独占锁线程个数不为0且当前线程不为执行线程,则返回-1
- 独占锁个数为0,共享锁被阻塞策略,获取当前线程的重入次数,若为0则readHolds对象中去除,并返回-1
- 共享锁个数超过最大值,抛出异常
- CAS操作状态,成功,则返回1;另外,计算线程重入次数并记录,如果第一个共享锁线程,则使用firstReader,firstReaderHoldCount,记录线程对象,个数;否则使用readHolds记录线程tid和重入次数信息,重置缓存信息cachedHoldCounter
tryReleaseShared方法
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firszhitReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
处理一下流程
- 当前线程为第一个共享,若共享次数为1,则置firszhitReader为null,否则firstReaderHoldCount减1
- 非第一个共享线程,若此线程重入次数为1,则从readHolds删除,否则其重入次数减1
- for循环自旋,CAS重置state状态,成功,则返回重置后状态是否为0
4.2.2 静态内部类NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false;
}
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
- 写锁无条件不被阻塞,而且写锁可以获取资源执行的可能时,读锁被阻塞;写锁必读锁有更高的优先级
- apparentlyFirstQueuedIsExclusive方法,AQS中方法,不应阻塞情况
- 队列为空
- 头节点的后继节点为空
- 头节点后继节点是共享节点
- 头节点后继节点的线程对象为空
4.2.3 静态内部类FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
是不是应该阻塞获取锁,都是判断是否有前驱节点,有前驱节点说明不是头节点,则不应该持有锁;
4.2.5读锁入口
public void lock() {
sync.acquireShared(1);
}
public void unlock() {
sync.releaseShared(1);
}
使用基类的共享锁的获取和释放流程
4.2.5 写锁入口
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
使用基类的独占锁的获取和释放流程
技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给与关注和点赞;如果文章存在错误,也请多多指教!