一、LockSupport
这个是AQS底层调用最频繁的类,这个类有两个重要的方法
- park:出于线程调度的考虑,禁用(暂停)当前线程,只要有许可可用
- unpark:为给定的线程提供许可证,这样给定线程就会变得可用。
这两个方法都是调用的本地方法来实现的
public static void park() {
UNSAFE.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
二、方法分析
AQS暴漏了5个方法,其他方法都是final修饰的。
- tryAcquire:互斥锁的获取
- tryRelease:互斥锁的释放
- tryAcquireShared:共享锁的获取
- tryReleaseShared:共享锁的释放
- isHeldExclusively:是否持有互斥锁
内部是一个双向链表组成的队列
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
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() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
三、同步互斥锁的实现的案例
最经典的实现是ReentrantLock,但是那个比较复杂,这次就模拟一个简单的实现
private static class Mutex implements Lock, java.io.Serializable {
// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Reports whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}
// Acquires the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// Releases the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// Provides a Condition
Condition newCondition() {
return new ConditionObject();
}
// Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
AQS机制推荐在内部类中继承AbstractQueuedSynchronizer
,比如本次实例中的Sync
主要重新了三个方法
- isHeldExclusively
- tryAcquire:通过CAS来处理锁
- tryRelease:主要就是把状态改成0,这里面没有任何加锁的代码,其实是因为能执行release的线程一定是只有一个。
每个实现其实也很简单isHeldExclusively
就是通过判断状态来进行的
一个简单的用法
Mutex mutex = new Mutex();
for (int i = 0; i < 1; i++) {
executorService.execute(() -> {
try {
mutex.lock();
TimeUnit.SECONDS.sleep(20);
System.out.println(Thread.currentThread().getName() + " get lock and execute");
} catch (Exception e) {
e.printStackTrace();
} finally {
mutex.unlock();
}
});
}
3.1、lock
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire
方法就是我们重写的方法,利用CAS更新stage字段,如果更新成功代表获取了锁,否则就是失败。
如果没有获取了锁,那么执行
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter就是构建一个Node,然后加入双向链表中
acquireQueued:这个就是暂停线程的主要方法了
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//队列的第一个元素是空节点,如果P是head,代表node就是第一个节点
//这个时候会再次尝试获取锁
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//park之前的检查,和信号状态的转换,最终会调用parkAndCheckInterrupt
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
最终调用了LockSupport.park(this);
阻塞了没有获取锁的线程
3.2、unlock
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;
}
首先会调用tryRelease
这个我们重写的方法,把状态设置为0,然后执行unparkSuccessor
方法
// 参数node是head节点,这个是实现了FIFO的思想,谁先阻塞,谁先进行解封
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//从head开始
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)
//最终找到第一个符合条件的阻塞的节点,然后去除他代表的线程,然后执行unpark
LockSupport.unpark(s.thread);
}
3.3、condition
这个其实没啥东西,就是分路通知,AQS里面有个内部类,然后一个condition我维护一个队列,这样生产者就只提醒消费者消费。
四、共享的实现的案例
经典的是CountDownlatch
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
CountDownLatch countDownLatch = new CountDownLatch(1);
executorService.execute(()->{
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
countDownLatch.await();
executorService.shutdown();
}
分析下内部类Sync
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
4.1、await
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
CountDownLatch countDownLatch = new CountDownLatch(1);
tryAcquireShared
就是重写的方法,如果state==0,那么就是返回1,代表CountDownLatch中的令牌已经消耗完了,这个时候就不会阻塞,await方法直接结束, 如果不是0,代表令牌还是存在的,所以需要等待被消费,调用countDownLatch.countDown();
方法,让令牌递减。
分析doAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这方法发和共享锁的acquireQueued
非常相似,不再分析了,可知会调用parkAndCheckInterrupt
方法让当前线程暂停
4.2、countDown
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
主要调用tryReleaseShared
放令牌-1. 如果最终令牌数量是0,那么返回true,否则返回false。
如果返回true,那么会调用doReleaseShared
方法。
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
其实很简单主要是调用unparkSuccessor
实现唤醒被await的线程