本文仅基于可重入的锁(ReentrantLock类)对AQS做分析,只考虑独占锁。
共享锁与独占锁的更多信息,以后再讨论。
AQS的节点包含了对前置节点的引用pre,后置节点的引用next,以及持有节点的线程thread.
static final class Node {
/**
pre节点,主要用来实现取消
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
volatile Node prev;
/**
next节点,主要用来实现阻塞/唤醒
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
volatile Node next;
//Node 节点 关联的线程
//1.重入时判断是否持有锁的线程
//2.取消阻塞时,unpark节点对应的线程
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
volatile Thread thread;
}
CLH队列中只有tail节点,通过tail节点就可以实现首尾相连的队列。
AQS中则使用head和tail节点实现队列。
入队时,通过tail节点构建CLH队列
出队时,只需要设置head节点(把当前成功获取锁的节点设置为head,则head节点为持有锁的节点)
入队:
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
//尝试直接设置tail入队,失败了走完整的enq入队逻辑
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//注意:如果没有调用过enq tail是为空的,下面的判断不成立
//也就是说,enq方法调用之后,尝试直接设置tail入队,才有机会
if (pred != null) {
node.prev = pred;
//如果入队成功,直接返回
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//完整的入队逻辑
enq(node);
return node;
}
/**
自旋入队(包含了初始化流程)
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//入队成功则返回,不成功继续自旋,最终生成队列
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
考虑enq不断自旋的情况:
线程1
1.第1次循环
tail为空,初始化
tail=head = new Node(无参构造,空的Node)
+------+
head(tail) | |
+------+
没有return,继续循环
2.第2次循环
如果成功入队
+------+ prev +-----+
head | | <---- | | tail(当前节点)
+------+ +-----+
返回前置节点,结束自旋
线程2
3.第1次循环
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
AQS为了支持可重入,使用了计数方式,对应属性是 volatile int state.
//AQS的同步状态
/**
* The synchronization state.
*/
private volatile int state;
①某个线
程获取锁的标志就是原子性的把state从某个期望状态(expect)改为指定状态(target)。
如果修改的时候state!=expect,说明state被其他线程改动了,其语义就是:锁被其他线程持有。
当前线程只能自旋/阻塞,直到持有锁的线程把状态改回expect,当前线程才能结束自旋,并且持有锁。
②支持可重入,则需要计数机制,每次重入,state+1,释放则state-1.
AQS使用LockSupport来阻塞/唤醒线程。
LockSupport针对每个线程操作,发给每个线程一个许可(permit),与blocker无关。
//如果得到许可,则立即返回,否则阻塞当前线程
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
//给指定传入的线程许可,解除阻塞
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
LockSupport不能被滥用。对当前线程调用unpark,可能导致AQS产生不可预知的情况。
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;
/**
* Created by 张三丰 on 2017\8\2 0002.
*/
public class ReentanceAndBlock {
static class CLHLock {
static class Node {
/**
* 节点的锁定状态,使用阻塞机制后,不需要这个状态了,获取不到锁的线程被阻塞,并在合适的时机被唤醒
*/
// volatile boolean isLocked = true;
//下一个节点
volatile Node next;
//持有节点的线程
volatile Thread thread;
public Node() {
}
public Node(Thread thread) {
this.thread = thread;
}
}
//可重入的状态,aqs使用了int 类型,然后使用了unsafe做cas操作。
// 这里不涉及unsafe,所以使用AtomicInteger
private volatile AtomicInteger state = new AtomicInteger();
private volatile Node tailNode;
private volatile Node headNode;
//这个值只需要持有锁的线程自己可见即可(锁重入时判断,只能是同一个线程)
// ,不需要volatile (其他线程见不见不影响)
private Thread holdLockThread;
private static final AtomicReferenceFieldUpdater TAIL_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(CLHLock.class, Node.class, "tailNode");
private static final AtomicReferenceFieldUpdater HEAD_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(CLHLock.class, Node.class, "headNode");
public void lock(int acquire) {
//1.尝试获取锁
if (tryLock(acquire)) {
return;
}
//2.入队
Node current = null;
for (; ; ) {
Node t = tailNode;
if (t == null) {
Node node = new Node();
if (TAIL_UPDATER.compareAndSet(this, null, node)) {
//初始化时,head节点是一个假的节点,绑定的是当前持有锁的线程
//以后也会一直保持绑定持有锁的节点
headNode = tailNode;
}
} else {
Node node = new Node(Thread.currentThread());
if (TAIL_UPDATER.compareAndSet(this, t, node)) {
t.next = node;
current = node;
break;
}
}
}
//3.自旋获取锁,或阻塞后等待唤醒
for (; ; ) {
//头节点是持有锁的节点,检查头节点,竞争锁
if (headNode.next == current) {
//前置节点是头节点,不能阻塞,一直自旋尝试获取锁
//这种情况是为了防止,被唤醒后,恰好有另外一个线程获取了锁,竞争失败的情况
if (tryLock(acquire)) {
headNode = current;
break;
}
// System.out.println("自旋获取锁~~");
} else {
//打印队列数
// System.out.println("====" + __getCount());
//阻塞当前线程,等待前置节点唤醒
LockSupport.park(this);
}
}
}
/**
* 获取队列数
*
* @return
*/
private int __getCount() {
int count = 1;
Node node = headNode.next;
while (node != null) {
count++;
node = node.next;
}
return count;
}
/**
* 定义获取锁的逻辑,AQS中此类由具体子类实现
* @param acquire
* @return
*/
private boolean tryLock(int acquire) {
int state = this.state.get();
if (state == 0 && this.state.compareAndSet(0, acquire)) {
//获取锁成功,不需要入队
holdLockThread = Thread.currentThread();
// __print1();
return true;
} else if (holdLockThread == Thread.currentThread()) {
//线程重入
this.state.set(this.state.get() + acquire);
return true;
}
return false;
}
private void __print1() {
if (headNode != null) {
Node node = headNode.next;
boolean isInCLH = false;
while (node != null) {
if (node.thread == Thread.currentThread()) {
isInCLH = true;
break;
}
node = node.next;
}
if (!isInCLH) {
System.out.println("厉害了,在唤醒的线程之前抢到了锁");
}
}
}
/**
*
*/
public void unlock(int acquire) {
// System.out.println("unlock" + Thread.currentThread());
if (tryRelease(acquire)) {
holdLockThread = null;
state.set(0);
unparkNext();
}
}
/**
* 定义释放锁的逻辑,AQS中此类由具体子类实现
* @param acquire
* @return
*/
private boolean tryRelease(int acquire) {
//只有持有锁的线程才能释放锁
if (Thread.currentThread() != holdLockThread) {
System.out.println("不是hold" + holdLockThread);
return false;
}
int newValue = state.get() - acquire;
//不会执行到
if (newValue < 0) {
throw new RuntimeException("state < 0");
}
//减去计数
state.set(newValue);
return newValue == 0;
}
private void unparkNext() {
Node headNode = this.headNode;
if (headNode != null) {
Node next = headNode.next;
if (next != null) {
//唤醒下个节点
LockSupport.unpark(next.thread);
}
}
}
}
static class CLHTester {
public static void main(String[] args) throws InterruptedException {
testLockAndUnlock();
testReentrance();
}
private static void testReentrance() {
System.out.println("testReentrance");
final Long[] number = {0L};
ExecutorService executorService = Executors.newFixedThreadPool(10);
CLHLock lock = new CLHLock();
for (int i = 0; i < 100; i++) {
executorService.execute(() -> {
lock.lock(1);
Long n = number[0];
if (n == 50) {
System.out.println("重入之前计数:" + lock.state);
System.out.println("锁重入2");
lock.lock(2);
System.out.println("重入之后计数:" + lock.state);
lock.unlock(2);
System.out.println("锁释放2");
System.out.println("释放之后计数:" + lock.state);
}
number[0] = n + 1;
System.out.println(number[0]);
lock.unlock(1);
});
}
executorService.shutdown();
}
private static void testLockAndUnlock() {
System.out.println("testLockAndUnlock");
//测试锁,循环100次,要求多线程共享的值能按照顺序+1,最终得到正确的结果 100
List sharedValue = Lists.newArrayList(new Integer(0));
ExecutorService executorService = Executors.newFixedThreadPool(50);
CLHLock lock = new CLHLock();
for (int i = 0; i < 100; i++) {
executorService.execute(() -> {
lock.lock(1);
//把数据+1
sharedValue.add(0, sharedValue.get(0) + 1);
//输出的结构必然是按顺序的
System.out.println(sharedValue.get(0));
// System.out.println(sharedValue.get(0) + "=========" + Thread.currentThread().toString());
lock.unlock(1);
});
}
executorService.shutdown();
while (!executorService.isTerminated()) {
}
}
}
}