AQS源码分析

来源:互联网 发布:淘宝全屏海报 编辑:程序博客网 时间:2024/06/11 18:21

AQS介绍

AbstractQueuedSynchronizer,AQS是用来构建锁和同步器的框架,Java并发工具包中的可重入所、读写锁都是基于AQS的子类构建的,使用的是组合并发继承,因为对用户(开发者)而言,我们不需要关心底层线程的调度和控制。

     *      +------+  prev +-----+       +-----+     * head |      | <---- |     | <---- |     |  tail     *      +------+       +-----+       +-----+head可看作持有锁的节点

源码分析

首先是AQS的双向队列节点源码:

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;        // 线程等待条件不会被当成同步队列节点,直到被唤醒signal,设置其值为0,重新进入节点阻塞状态        static final int CONDITION = -2;        static final int PROPAGATE = -3;        // 默认值为0,阻塞状态        volatile int waitStatus;        volatile Node prev;        volatile Node next;        // 当前线程        volatile Thread thread;        // 表示共享模式还是独占模式        Node nextWaiter;}

独占模式的获取锁操作,我们可以参考可重入锁的NonfairSync实现:

    final void lock() {        if (compareAndSetState(0, 1))            setExclusiveOwnerThread(Thread.currentThread());        else            acquire(1);    }    public final void acquire(int arg) {        if (!tryAcquire(arg) &&             acquireQueued(            // 向等待队列最后加入一个独占模式的节点,            addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }    private Node addWaiter(Node mode) {        Node node = new Node(Thread.currentThread(), mode);        // Try the fast path of enq; backup to full enq on failure        Node pred = tail;        if (pred != null) {            node.prev = pred;            // cas 操作,防止多线程相互干扰            if (compareAndSetTail(pred, node)) {                pred.next = node;                return node;            }        }        // 设置相关的信息        enq(node);        return node;    }    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 {            if (failed)                cancelAcquire(node);        }    }    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 {            // 将前驱结点设为SIGNAL,下次判断前驱节点直接挂起            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);        }        return false;    }

获取锁总结:

  1. addWaiter,将node节点加入到同步队列后面
  2. acquireQueued,获取该node节点的同步状态,自旋直到node的前驱结点是头结点并且能够获得锁,否则,设置前驱节点为阻塞状态,需要被唤醒才能返回。

释放锁流程:

    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;    }    private void unparkSuccessor(Node node) {        int ws = node.waitStatus;        if (ws < 0)            compareAndSetWaitStatus(node, ws, 0);        // 后续节点        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)            LockSupport.unpark(s.thread);    }

释放锁总结:
释放头结点的锁,若后续节点可被唤醒,则唤醒后续节点,否则从队列后面开始找可唤醒的节点

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 邮箱里的文件过期了怎么办 小米手机邮件发不了怎么办 有人加我qq邮箱怎么办 文件太大发邮件太慢怎么办 爱又米发信息怎么办 手机上电子邮件己停止运行怎么办 苹果电子邮件密码忘了怎么办 玩游戏时电脑烫怎么办 qq邮箱独立密码忘记了怎么办 qq邮箱中转站容量不足怎么办 qq邮箱忘记密码了怎么办 qq邮箱超大附件过期怎么办 忘记qq邮箱独立密码怎么办 网易邮箱账号忘了怎么办 微信登录密码忘了怎么办 微信太久没登录登录不上怎么办 邮箱独立密码忘记了怎么办 苹果设置id没有邮箱怎么办 苹果手机设置id没有邮箱怎么办 邮箱的附件过期了怎么办 邮箱里的附件过期了怎么办 邮箱中附件过期了怎么办 扣扣邮箱附件过期怎么办 公司网页版邮箱进不去了怎么办 农行客户端密码忘记了怎么办 中国银行客户端密码忘记了怎么办 建行客户端登录密码忘记了怎么办 中国移动客户端密码忘记了怎么办 再歪一点授权码绑定怎么办 网易邮箱号忘了怎么办 忘记支付宝账号和密码怎么办 支付宝账号密码忘了怎么办 发邮箱文件超2g怎么办 报考计算机二级邮箱不存在怎么办 苹果邮箱登录要imap密码怎么办 注销微信支付后怎么办 手机卡注销后支付宝怎么办 12306手机邮箱都换了怎么办 网易手机邮箱手机换了怎么办 崩坏3号被盗了怎么办 qq账号被永久冻结了怎么办