理解AQS(AbstractQueuedSynchronizer)源代码分析

目录

AQS简介

ReentrantLock源码实现【独占锁】

公平锁非公平锁区别

源码分析

公平锁 

非公平锁

CountdownLatch源码实现【共享锁】

初始化

await方法

countDown方法


AQS简介

AbstractQueuedSynchronizer位于java.util.concurrent.locks包下,是一个用于构建锁和同步器的框架,是除了java自带的synchronized之外的锁机制。ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier等均是基于AQS构建出来的。

其维护了:

  • 一个volatile类型的state变量,用于描述锁是否被占有,如果未占有=0,如果已占有>=1,首次占用=1,每重入一次+1,因此重入加锁多少次就要解锁多少次,每次解锁-1,直到0时释放锁
  • 一个FIFO的线程等待队列,当锁已被某线程占用,再次发生线程竞争,该线程会进入队列排队
  • 线程拥有者,存储占用当前锁的线程,方便重入
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private volatile int state;// 锁状态,加锁成功则为1,重入+1,解锁为0

    private transient volatile Node head;// 队首

    private transient volatile Node tail;// 队尾

    /**
     * Node结点
     */
    static final class Node {

        volatile Node prev;// 上一个结点

        volatile Node next;// 下一个节点

        volatile Thread thread;// 节点线程
    }

}

其继承的AbstractOwnableSynchronizer类维护了线程持有者

public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {

    /**
     * 锁的线程持有者
     */
    private transient Thread exclusiveOwnerThread;
}

其功能主要分为独占锁和共享锁:

  • 独占锁:一个时间只能有一个线程拿到锁,可重入状态以外,state只有0和1两种。例如ReentrantLock,state!=0且持有锁的线程不是当前线程,则拿不到锁
  • 共享锁:一个时间可以有多个线程拿到锁协同工作,state可以是任意正整数。例如CountDownLatch,state!=0就拿不到锁

自定义同步器首先继承AQS类,然后重写以下方法:

  • protected boolean tryAcquire(int arg):独占方式获取锁。成功返回true
  • protected boolean tryRelease(int arg):独占方式释放锁。成功返回tre
  • protected int tryAcquireShared(int arg):共享方式获取锁。<0失败,=0成功但没有可用资源,>0成功且有剩余资源
  • protected boolean tryReleaseShared(int arg):共享方式解锁。成功返回true

前言概述

        ReentrantLock,初始化state=0,表示锁未被占用,当过来一个线程调用tryAcquire以独占方式获取锁,此时state+1,锁被占用后,其他线程再来想获取,就会失败,然后去队列中排队,按照先进先出的方式依次获取锁。如果新来的线程还是刚刚已经占用锁的线程,则可以直接获取锁,并且state再+1,即可重入锁。ReentainLock.unLock方法调用tryRelease以独占方式释放锁,此时state-1,直到state=0,锁完全释放。

        CountDownLatch,一般在一个主线程下存在N个子线程分别执行,初始化state=N,每一个子线程执行结束,调用countDown,state-1,所有子线程执行完毕,此时state=0,调用tryReleaseShared方法释放锁,其他线程才能获取锁。使用await方法进行阻塞,内部采用公平锁机制,新来的线程想要获取锁,如果发现state!=0,就从队尾入队,如果发现自己是队头,就采用tryAcquireShared方法获取锁,获取不到就死循环一直获取,新来的一直从队尾入队,直到state=0,拿到锁,队列里的线程逐个变成队头,逐个拿到锁,逐个执行。

ReentrantLock源码实现【独占锁】

ReentrantLock默认为非公平锁,其构造方法可以根据入参的true和false手动设定公平/非公平

/*
 * 默认非公平锁
 */
public ReentrantLock() {
    sync = new NonfairSync();
}
/*
 * 手动指定 公平/非公平
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

公平锁非公平锁区别

 公平锁:严格遵循先来后到,如果当前锁被占用且队列中还有没出队的线程,就乖乖去排队,一直到轮到自己。

非公平锁:唯一的区别就是,线程来了,他如果发现没加锁,不会去检查队列里有没有,会直接CAS获取锁,获取到了就是获取到了,那么队列里的就得一直排队,因此有被饿死的风险,非公平锁可以在一定程度上提高吞吐量。

源码分析

reentrantLock.lock();

   


公平锁 

final void lock() {
    acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();// 获取当前线程
            int c = getState();// 查看锁的状态
            if (c == 0) {// 如果没被占用继续往下走
                // 如果不需要排队,就开始CAS,CAS也成功了,就把当前线程作为持有这把锁的线程
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果线程已经被占用了,而且是当前线程,那就给state继续+1
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
/**
 * 查询是否需要放到队列里进行排队
 * 队列没有被初始化
 */
public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
/**
 * 入队操作,直接插入队尾
 */
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
/**
 * 将未拿到锁的线程入队【插入队尾】
 * tail.next = node;node.prev = tail
 */
private Node enq(final Node node) {
        for (;;) {// 死循环
            Node t = tail;
            if (t == null) {
                // 当队列尾结点为空,则新建一个空的Node节点,并赋值给head和tail
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 第一遍循环结束,tail!=null,于是开始走下面的代码
                node.prev = t;// 尾结点后面插入node,即入队操作
                if (compareAndSetTail(t, node)) {
                    t.next = node;// 双向链表,next也要赋值
                    return t;// 退出循环
                }
            }
        }
    }
/**
 * 线程阻塞(在最终阻塞前,会自旋两次,仍然获取不到锁再进行阻塞)
 */
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;
                }
                // shouldParkAfterFailedAcquire第一次执行返回false,但内部会改变状态
                // 第二次执行返回true,然后执行parkAndCheckInterrupt阻塞线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

非公平锁

/**
 * 新的线程来了,直接CAS,才不管队列里有没有,如果队列里有,一直轮不到 就等着
 */
final void lock() {
    // CAS比较state,当前为0则加锁成功,并设置当前线程为锁的拥有者
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    // 否则开始入队操作
    else
        acquire(1);
}
/*
 * 看当前锁是否可占用,如果可以,不排队,直接占【区别于公平锁】
 */
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;
        }

 当线程交替执行,其每次判断的时候总能拿到锁,因此不用往队列里面放,也就不用切换内核态挂起线程,一直在JDK层面运行,因此非常快。

CountdownLatch源码实现【共享锁】

在完成一组线程操作之前,允许一个或多个线程等待,直到前一组全部完成。内部采用公平锁和共享锁机制。

初始化

构造方法:传入一个初始化数值>=0,由于Sync类继承的AQS,因此初始化是修改AQS中state值为当前值,对应CountDownLatch计数器初始值。

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
public class CountDownLatch {
    // Sync为CountDownLatch的内部类,同时继承AQS,因此CountDownLatch底层由AQS实现
    private static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            setState(count);
        }
    }
}
protected final void setState(int newState) {
        state = newState;
    }

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)// 共享锁方式获取锁,<0说明没拿到锁,往下执行
            doAcquireSharedInterruptibly(arg);
    }
/**
 * 共享锁方式获取锁,state=0说明拿到锁返回1,拿不到返回-1
 */
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 新建一个共享结点,并且入队尾
        final Node node = addWaiter(Node.SHARED);// static final Node SHARED = new Node();
        boolean failed = true;
        try {
            for (;;) {// 自旋
                final Node p = node.predecessor();// 获取node的上一个结点
                if (p == head) {
                    // 如果node上一个结点就是head,说明node在队首
                    // 那就直接开始获取锁
                    int r = tryAcquireShared(arg);
                    // 如果r=-1,说明没拿到锁,那就继续死循环等着
                    if (r >= 0) {
                        // 如果获取锁成功,就把当前结点设置为head头结点,给下一个结点把队首位置腾出来
                        setHeadAndPropagate(node, r);
                        p.next = null;// 方便GC回收旧的head结点
                        failed = false;
                        return;
                    }
                }

                // 如果还是没拿到锁,找到有效的前驱结点后,使用park方法将线程挂起,直到被unpark
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

该Node是否可以被安心挂起。

判断自己的前驱waitStatus是否已经是SIGNAL,如果是,就可以放心挂起了,如果不是,需要找到有效的前驱

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            // 前驱结点的waitStatus已经=SIGNAL,就等着释放了
            return true;
        if (ws > 0) {
            // 如果发现前驱>=,即=CANCELLED,表明前驱线程因超时或中断而取消执行
            // 因此直接跳过,寻找后面符合条件的前驱结点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // 前驱的waitStatus=0或PROPAGATE,将其设置为SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

countDown方法

public void countDown() {
        sync.releaseShared(1);
    }
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {// 判断计数器是否=0,=0说明拿到锁了,往下走
            doReleaseShared();
            return true;
        }
        return false;
    }
/**
 * 判断AQS中state的值是否>0,若>0则减1
 */
protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                // 如果state已经=0,说明已经有其他线程发出唤醒信号,这里无需再次唤醒
                if (c == 0)
                    return false;
                int nextc = c-1; // 大于0就-1
                if (compareAndSetState(c, nextc)) // CAS比较state
                    // 如果state还是0,那就返回true开始释放共享锁
                    return nextc == 0;
            }
        }
private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                // Node节点的waitStatus变量如果为SIGNAL,说明后面挂起的结点需要被唤醒
                if (ws == Node.SIGNAL) {
                    // 然后把waitStatus状态重置为0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;
                    unparkSuccessor(h);// 唤醒后继结点
                }
                else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            if (h == head) 
                break;
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值