基于JDK1.8进行分析的。
在分析AbstractQueuedSynchronizer之前先分析一下LockSupport这个类,以为这个是关于这些锁中最基本的锁。
LockSupport
类继承结构
public class LockSupport
可以看见自己就是一个类,而且无继承无实现。
成员属性
//unsafe中的,以后常见
private static final sun.misc.Unsafe UNSAFE;
//表示内存偏移地址
private static final long parkBlockerOffset;
//表示内存偏移地址
private static final long SEED;
//表示内存偏移地址
private static final long PROBE;
//表示内存偏移地址
private static final long SECONDARY;
静态代码块
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
通过上面代码可以看出,基本都是对Thread的四个偏移地址的成员属性进行初始化。
构造方法
private LockSupport() {}
发现该类的构造方法只有一个,并且被私有化。
成员方法
park
public static void park(Object blocker) {
//当前线程
Thread t = Thread.currentThread();
//设置blocker
setBlocker(t, blocker);
//调用park方法
UNSAFE.park(false, 0L);
//重新可运行后再此设置Blocker
setBlocker(t, null);
}
public static void park() {
UNSAFE.park(false, 0L);
}
整个类中一共有两个关于park的方法,形式也就是重载。底层都是通过调用UNSAFE的park方法实现的。两者的区别在于有没有设置blocker。
其中涉及到方法setBlocker,源码如下:
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
这个park方法实现的效果就是阻塞当前线程,然后当调用unpark方法之后,那么在恢复这个线程。但是后者不是安全的,调用的时候,必须要保证恢复的线程存活。
parkNanos
此函数表示在许可可用前禁用当前线程,并最多等待指定的等待时间。
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
代码和之前的park类似,此处不再赘述。
parkUntil
此函数表示在指定的时限前禁用当前线程,除非许可可用。
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
unpark
此函数表示如果给定线程的许可尚不可用,则使其可用。如果线程在 park 上受阻塞,则它将解除其阻塞状态。否则,保证下一次调用 park 不会受阻塞。如果给定线程尚未启动,则无法保证此操作有任何效果。
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
下面我们回想一下wait和notify的使用。首先来讲,调用wait之后,在调用对应线程的notify方法,可以将该线程从休眠状态改为就绪状态,反之,如果先调用notify,那么再次调用同一个线程的wait方法时,那么此时是不能将该线程转为就绪状态的。然而在该类中则不是这样。我们下面来看一下。
package demo;
import java.util.concurrent.locks.LockSupport;
/**
* LockSupport代码测试例程
* @ClassName: LockSupportDemo
* @Description: TODO
* @author BurgessLee
* @date 2019年4月22日
*
*/
public class LockSupportDemo {
public static void main(String[] args) {
System.out.println("Thead-current:" + Thread.currentThread().getName());
MyThreadTest myThreadTest = new MyThreadTest(Thread.currentThread());
myThreadTest.start();
System.out.println("before park");
//先调用unpark在调用park打开注释
// try {
// Thread.sleep(3000);
// } catch (Exception e) {
// e.printStackTrace();
// }
//获取许可
LockSupport.park("LockSupportDemo");
System.out.println("after park");
}
}
//先调用unpark再调用park结果
//Thead-current:main
//before park
//before unpark
//after unpark
//after park
//先调用unpark再调用park
//class MyThreadTest extends Thread{
// private Object obj;
//
// public MyThreadTest(Object obj) {
// super();
// this.obj = obj;
// }
//
// @Override
// public void run() {
// System.out.println("before unpark");
// LockSupport.unpark((Thread)obj);
// System.out.println("after unpark");
// }
//}
//先调用park在调用unpark结果
//Thead-current:main
//before park
//before unpark
//Blocker info LockSupportDemo
//after park
//Blocker info null
//after unpark
//先调用park在调用unpark
//class MyThreadTest extends Thread{
// private Object obj;
//
// public MyThreadTest(Object obj) {
// super();
// this.obj = obj;
// }
//
// @Override
// public void run() {
// System.out.println("before unpark");
// try {
// Thread.sleep(3000);
// } catch (Exception e) {
// e.printStackTrace();
// }
// //获取blocker
// System.out.println("Blocker info " + LockSupport.getBlocker((Thread) obj));
// //释放许可
// LockSupport.unpark((Thread) obj);
// //休眠500ms,保证先执行park中的setBlocker(t, null);
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// //再次获取blocker
// System.out.println("Blocker info " + LockSupport.getBlocker((Thread) obj));
// System.out.println("after unpark");
// }
//
//}
通过上面代码的演示,基本说明了,调用park和unpark的先后顺序颠倒,不会产生wait和notify的效果。具体可以自己本地测试。
当然当线程调用了park方法之后,如果此时调用该线程的interrupt方法,也会产生和调用unpark一样同步的效果。以上大致就是LockSupport整个源码的分析过程了。下面开始看我们的主角AQS。
AbstractQueuedSynchronizer
简单来讲,该类的数据结构是通过队列实现的。默认有一个Sync队列,是一个双向链表的结构,有Head和Tail。另外还有一个Condition队列,当然不是必须的,是一个单向链表。下面我们具体来看看这个类的实现。
继承结构
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
继承了类AbstractOwnableSynchronizer。后者是一个抽象类,源码如下:
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 3737899427754241961L;
protected AbstractOwnableSynchronizer() { }
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
可以看到实现了Serializable接口。主要有一个成员属性exclusiveOwnerThread,也就是独占资源的线程,以及对应有set、get方法。
内部类
Node
static final class Node {
//共享模式
static final Node SHARED = new Node();
//独占模式
static final Node EXCLUSIVE = null;
// 结点状态
// CANCELLED,值为1,表示当前的线程被取消
// SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
// CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
// PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行
// 值为0,表示当前节点在sync队列中,等待着获取锁
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(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
//构造函数
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
ConditionObject
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
//more....
可以看到该类实现了Condition接口。源码如下:
public interface Condition {
//等待,当前线程在接到信号或被中断之前一直处于等待状态
void await() throws InterruptedException;
//等待,当前线程在接到信号之前一直处于等待状态,不响应中断
void awaitUninterruptibly();
//等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态
long awaitNanos(long nanosTimeout) throws InterruptedException;
//等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit) throws InterruptedException;
//等待,当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待线程。如果所有的线程都在等待此条件,则选择其中的一个唤醒。
//在从await返回之前,该线程必须重新获取锁。
void signal();
//唤醒所有等待线程。如果所有的线程都在等待此条件,则唤醒所有线程。
//在从 await 返回之前,每个线程都必须重新获取锁。
void signalAll();
}
成员属性
//用于序列化
private static final long serialVersionUID = 7373984972572414691L;
//头结点
private transient volatile Node head;
//尾节点
private transient volatile Node tail;
//状态值
private volatile int state;
//自旋时间
static final long spinForTimeoutThreshold = 1000L;
下面这些在LockSupport中已经介绍过了,此处不再赘述。
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
构造方法
protected AbstractQueuedSynchronizer() { }
只有一个,而且被protected修饰。
成员方法
首先getState和setState就不在介绍了。可以说主要的核心的方法一共有四个。罗列起来就是:
protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)
因为整体是通过队列实现的,那么我首先来看队列操作。
compareAndSetHead(Node update)
设置队列的头结点,在enq方法被调用
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
setHead(Node node)
重新设置队列头节点,只在acquire中被调用
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
compareAndSetTail(Node expect, Node update)
设置队列尾节点,在enq中有被调用。
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
enq(final Node node)
普通节点入队操作,调用方法。在addWaiter被调用
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 空节点的时候,这时进行初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {//尾节点不为空,通过CAS添加到队列的尾部
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
addWaiter(Node mode)
添加当前线程到队列的尾节点。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//当前线程
Node pred = tail;
if (pred != null) {//已经被初始化,那么通过CAS添加到队列尾部
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//入队操作
enq(node);
return node;
}
看来的对了的操作,下面我们来看看独占锁具体是怎么实现的。
acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
独占模式获取资源,并且中断无效。下次继续。
首先来看这个整体的实现逻辑。
- 1. 先调用tryAcquire方法,尝试获取独占锁,返回true,表示获取到锁;如果没有获取成功,执行acquireQueued方法。
- 2. 调用acquireQueued方法,先调用addWaiter方法为当前线程创建一个节点node(上面队列操作中已经介绍过),并插入队列中,
- 3.然后调用acquireQueued方法去获取锁,如果不成功,就会让当前线程阻塞,当锁释放时才会被唤醒。(后面介绍)
- 4.acquireQueued方法返回值表示在线程等待过程中,是否有另一个线程调用该线程的interrupt方法,发起中断。(后面介绍)
tryAcquire(int arg)
尝试获取锁,可见这个方法直接抛出异常,而且用protected关键字修饰,是希望子类去实现该方法,否则抛出异常。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
该方法的作用是让当前线程尝试获取锁,如果获取到锁,就会返回true,并更改锁资源。没有获取到锁返回false。
上面acquire方法中,如果获取锁成功,那么为当前线程创建一个Node节点的实例执行addWaiter方法入队,之后调用acquireQueued方法尝试去获取锁。该acquireQueued的返回值代表在线程等待过程中,是否有另一个线程调用该线程的interrupt方法,发起中断。如果是,那么直接selfInterrupt执行。
我们先看一下selfInterrupt方法源码。
selfInterrupt()
作用是使得当前线程中断。
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
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;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// failed为true,表示发生异常,非正常退出
// 则将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了。
if (failed)
cancelAcquire(node);
}
}
过程大致如下:首先我们看到的主体部分是一个for的死循环,如果当前节点的前驱也就是节点p,如果是头节点,并且成功获取锁,那么直接将当前节点设置为头结点,直接执行。否则,那么会调用shouldParkAfterFailedAcquire和parkAndCheckInterrupt两个方法。为了不影响分析,我们先看一下这两个方法的源码。
shouldParkAfterFailedAcquire(Node pred, Node node)
方法的返回值决定是否要阻塞当前线程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//当前线程上一个节点的状态值
int ws = pred.waitStatus;
//如果状态是SINGNAL,那么返回true,当前线程应该被阻塞
if (ws == Node.SIGNAL)
return true;
//如果前一个节点状态是Node.CANCELLED(大于0就是CANCELLED),
//表示前一个节点所在线程已经被唤醒了,要从队列中移除CANCELLED的节点。
//所以从pred节点一直向前查找直到找到不是CANCELLED状态的节点,并把它赋值给node.prev,
//表示node节点的前一个节点已经改变。
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 此时前一个节点pred的状态只能是0或者PROPAGATE,不可能是CONDITION状态
// CONDITION是特殊状态,只在condition列表中节点中存在,队列中不存在这个状态的节点
// 将前一个节点pred的状态设置成Node.SIGNAL
//这样在下一次循环时,就是直接阻塞当前线程
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt()
阻塞当前线程,线程被唤醒后返回当前线程中断状态。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
通过源码可以看到,阻塞当前线程,然后当线程被唤醒后,返回线程的中断状态。
cancelAcquire(Node node)
将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了。
private void cancelAcquire(Node node) {
//数据校验
if (node == null)
return;
node.thread = null;
//node之前的节点
Node pred = node.prev;
// 跳过那些已取消的节点,在队列中找到在node节点前面的第一次状态不是已取消的节点
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 记录pred原来的下一个节点,用于CAS函数更新时使用
Node predNext = pred.next;
// 将node节点状态设置为已取消Node.CANCELLED;
node.waitStatus = Node.CANCELLED;
// 如果node节点是队列尾节点,那么就将pred节点设置为新的队列尾节点
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);// 释放node的前一个结点
}
node.next = node; // help GC
}
}
下面我们继续看里面调用的方法
unparkSuccessor(Node node)
释放后继节点
private void unparkSuccessor(Node node) {
//node的状态
int ws = node.waitStatus;
//为SIGNAL -1 或 CONDITION -2 或 PROPAGATE -3
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//获取node节点的下一个结点
Node s = node.next;
//下一个结点为空或者下一个节点的等待状态大于0,即为CANCELLED
if (s == null || s.waitStatus > 0) {
s = null;
//向前寻找节点
for (Node t = tail; t != null && t != node; t = t.prev)
//找到等待状态小于等于0的结点,找到最前的状态小于等于0的结点
if (t.waitStatus <= 0)
s = t;
}
//该结点不为为空,释放许可
if (s != null)
LockSupport.unpark(s.thread);
}
以上也就是整个acquire独占锁实现的过程。下面我们继续分析release释放独占锁的过程。
release(int arg)
独占模式下,释放独占锁。
public final boolean release(int arg) {
// 调用tryRelease方法,尝试去释放锁,由子类具体实现
if (tryRelease(arg)) {
//如果队列头节点的状态不是0,那么队列中就可能存在需要唤醒的等待节点。
//还记得我们在acquireQueued(final Node node, int arg)获取锁的方法中,
//如果节点node没有获取到锁,
//那么我们会将节点node的前一个节点状态设置为Node.SIGNAL,
//然后调用parkAndCheckInterrupt方法
//将节点node所在线程阻塞。
//在这里就是通过unparkSuccessor方法,
//进而调用LockSupport.unpark(s.thread)方法,唤醒被阻塞的线程
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
从源码可以看出调用了tryRelease方法,源码如下:
tryRelease(int arg)
可见和tryAcquire方法一样,需要子类去实现,否则抛出异常。
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
返回true表示锁资源完全释放了,返回false表示还持有锁资源。
注:对于独占锁来说,同一时间只能有一个线程持有这个锁,但是这个线程可以重复地获取锁,因为被锁住的模块,再次进入另一个被这个锁锁住的模块,是允许的。这个就做可重入性,所以对于可重入的锁释放操作,也需要多次。
代码中unparkSuccessor方法在前面已经介绍过了,所以此处不再赘述。下面我们来看共享锁。涉及到的方法自然就是acquireShared。源码如下:
acquireShared(int arg)
用来获取共享锁。
public final void acquireShared(int arg) {
//尝试去获取共享锁,如果返回值小于0表示获取共享锁失败
if (tryAcquireShared(arg) < 0)
//继续获取共享锁
doAcquireShared(arg);
}
我们接着看源码中涉及调用的代码。
tryAcquireShared(int arg)
可见和acquire方法类似。需要子类实现,否则抛出异常。返回值大于等于0,表示获取共享锁成功。
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
doAcquireShared(int arg)
获取共享锁,获取失败,则会阻塞当前线程,直到获取共享锁返回
private void doAcquireShared(int arg) {
//为当前线程创建共享锁节点node
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//如果节点node前一个节点是同步队列头节点。
//就会调用tryAcquireShared方法尝试获取共享锁
int r = tryAcquireShared(arg);
//如果返回值大于0,表示获取共享锁成功
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//如果节点p的状态是Node.SIGNAL,
//就是调用parkAndCheckInterrupt方法阻塞当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//failed为true,表示发生异常,
//则将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了
if (failed)
cancelAcquire(node);
}
}
与独占锁相比来看,不同如下:
- doAcquireShared方法,调用addWaiter(Node.SHARED)方法,为当前线程创建一个共享模式的节点node。而acquireQueued方法是由外部传递来的。
- doAcquireShared方法没有返回值,acquireQueued方法会返回布尔类型的值,是当前线程中断标志位值
- 最大的区别是重新设置队列头的方法不一样。doAcquireShared方法调用setHeadAndPropagate方法,而acquireQueued方法调用setHead方法。
setHeadAndPropagate(Node node, int propagate)
重新设置队列头,如果队列头的下一个节点为null或者共享模式,那么就要唤醒共享锁上等待的线程
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
//重新设置对头节点
setHead(node);
// 如果propagate大于0
if (propagate > 0 ||
h == null ||
h.waitStatus < 0 ||
(h = head) == null ||
h.waitStatus < 0) {
Node s = node.next;
//如果节点s是空或者共享模式节点,那么就要唤醒共享锁上等待的线程
if (s == null || s.isShared())
doReleaseShared();
}
}
releaseShared(int arg)
作用是释放共享锁
public final boolean releaseShared(int arg) {
// 尝试释放共享锁
if (tryReleaseShared(arg)) {
// 唤醒等待共享锁的线程
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared(int arg)
需要子类去实现,否则抛出异常。
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
返回true表示已经完全释放锁资源,返回false,表示还持有锁资源。
doReleaseShared()
执行释放动作。
private void doReleaseShared() {
//死循环
for (;;) {
//头节点
Node h = head;
//队列中有其他元素
if (h != null && h != tail) {
//得到节点h的状态
int ws = h.waitStatus;
// 如果状态是Node.SIGNAL,就要唤醒节点h后继节点的线程
if (ws == Node.SIGNAL) {
//将节点h的状态设置成0,如果设置失败,就继续循环,再试一次。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
//唤醒节点h后继节点的线程
unparkSuccessor(h);
}
//如果节点h的状态是0,就设置ws的状态是PROPAGATE。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
//如果同步队列头head节点发生改变,继续循环,
//如果没有改变,就跳出循环
if (h == head)
break;
}
}
代码中注释内容已经很清楚,此处不再赘述。
剩下的方法await和singal以及singalAll以后在介绍。看了这么多源码,接下来看看DEMO演示。
package demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.junit.Test;
/**
* AbstractQueuedSyncrhonizer代码测试
* @ClassName: AbstractQueuedSyncrhonizerDemo
* @Description: TODO
* @author BurgessLee
* @date 2019年4月23日
*
*/
public class AbstractQueuedSyncrhonizerDemo {
@Test
public void test01() {
Lock lock = new ReentrantLock();
// for(int i = 0; i < 100; i ++) {
// MyThreadTest t = new MyThreadTest("thread-" +i, lock);
// t.start();
// }
MyThreadTest t1 = new MyThreadTest("thread-1",lock);
MyThreadTest t2 = new MyThreadTest("thread-2",lock);
t1.start();
t2.start();
}
}
class MyThreadTest extends Thread{
private Lock lock;
public MyThreadTest(String name,Lock lock) {
super(name);
this.lock = lock;
}
@Override
public void run() {
lock.lock();
try {
System.out.println(Thread.currentThread() + "running....");
}finally {
lock.unlock();
}
}
}
输出结果如下:
Thread[thread-1,5,main]running....
Thread[thread-2,5,main]running....