zoukankan      html  css  js  c++  java
  • 从源码分析:Java中的AQS

    获取锁

    public final void acquire(int arg) {
        // 首先通过tryAcquire尝试获得锁
        // 如果未能成功获得锁,则进入acquireQueued
        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
        // 获得当前队列的尾部节点
        // tail 为AQS的属性
        Node pred = tail;
        if (pred != null) {
            // 如果尾部节点不为null,则将当前节点的前节点设为刚刚获取到的队列的尾节点
            node.prev = pred;
            // 如果在以上这段时间内,尾节点没有发生过变化,则直接通过CAS设置尾节点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果通过CAS设置尾节点失败,则通过循环CAS来设置新的尾节点
        enq(node);
        return node;
    }
    
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 尝试获得前节点,如果获取失败抛出NullPointerException异常
                final Node p = node.predecessor();
                // 如果前节点是头节点,则tryAcquire()
                if (p == head && tryAcquire(arg)) {
                    // 将该节点设为头节点,同时将该node的thread与prev都设为null
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    //检查并更新无法获取的节点的状态,如果线程应该阻塞,则返回true。
    // 这是在acquire循环中的主要信号控制方法,需要pred == node.prev
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
            * This node has already set status asking a release
            * to signal it, so it can safely park.
            */
            // 此节点已设置状态,要求释放信号,因此可以安全停放。
            return true;
        if (ws > 0) {
            /*
            * Predecessor was cancelled. Skip over predecessors and
            * indicate retry.
            */
            // 前节点被取消,则跳过前节点并重试
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
            * waitStatus must be 0 or PROPAGATE.  Indicate that we
            * need a signal, but don't park yet.  Caller will need to
            * retry to make sure it cannot acquire before parking.
            */
            // waitStatus必须为0或者PROPAGATE,说明我们需要一个信号,但不需要park。
            // 调用者会需要重试来确保在park前不会acquire。
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    // 一个便利方法来实现park并判断是否interrupted
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    

    释放锁

    // 以独占模式释放锁。
    public final boolean release(int arg) {
        // 首先tryRelease尝试释放锁
        if (tryRelease(arg)) {
            // 如果释放成功,将当前的头节点取出
            Node h = head;
            // 如果当前头节点不为空且其waitStatus不为0
            if (h != null && h.waitStatus != 0)
                // 
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    private void unparkSuccessor(Node node) {
        /*
        * If status is negative (i.e., possibly needing signal) try
        * to clear in anticipation of signalling.  It is OK if this
        * fails or if status is changed by waiting thread.
        */
        // 将当前节点状态归零
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
    
        /*
        * Thread to unpark is held in successor, which is normally
        * just the next node.  But if cancelled or apparently null,
        * traverse backwards from tail to find the actual
        * non-cancelled successor.
        */
        // 取出下一个节点
        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;
        }
        // 如果此时s指向的节点不为空,则唤醒该节点
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    
  • 相关阅读:
    avalov+require实现tab栏
    动态加载js,css
    Zepto.js
    Linux 的文件和目录管理类命令
    shell 的基本理解
    Linux 日期时间命令
    Linux 关机命令
    type 命令
    命令类型即使用帮助
    cd 命令
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744369.html
Copyright © 2011-2022 走看看