zoukankan      html  css  js  c++  java
  • AQS的不公平锁源码

    同步器节点的waitStatus解释
    CANCELLED 取消状态
    
    SIGNAL -1 等待触发状态,前节点可能是head或者前节点为取消状态CANCELLED
    
    CONDITION -2 等待条件状态,在等待队列中
    
    PROPAGATE -3 状态需要向后传播
    
    
    //不公平锁的lock函数
    static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
            final void lock() {
                //首先尝试获得锁,如果获得,就将独占锁的内部变量改为当前线程
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                //如果没有获得锁,就转到AQS类的acquire函数中
                    acquire(1);
            }
    
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    }
    
    //AQS类的acquire函数,传入参数1
    public final void acquire(int arg) {
            if (!tryAcquire(arg) &&         //查看是否能够获得锁,就是上面的NonfairSync上面的tryAcquire方法,这个方法重写了虚类方法,如果不能,将返回false,查看&&后的方法
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
    }
    
    //ReentrantLock类的nonfairTryAcquire函数
     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;                       //这个时候的情况就是锁是被别的线程占领的
    }
    
    //AQS类的addWaiter函数,传入参数Node.EXCLUSIVE
    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)) {        //cas操作设置尾节点
                    pred.next = node;
                    return node;
                }
            }
            enq(node);          //当cas操作不成,或者队列中没有别的线程的时候用这个
            return node;
    }
    //AQS类的enq函数,传入参数等待假如阻塞队列的线程
    private Node enq(final Node node) {
        //死循环,不加入不出来
            for (;;) {  
                Node t = tail;      //获取尾节点
                if (t == null) { // 如果尾节点是空,说明这个是第一个阻塞线程
                    if (compareAndSetHead(new Node()))  //设置阻塞线程的头部,一个没有任何用处的头部
                        tail = head;                    //尾巴就是头,然后再次循环
                } else {                                
                    node.prev = t;                      
                    if (compareAndSetTail(t, node)) {      //cas操作将新的线程插入到tail,如果不成功将会再次循环过来
                        t.next = node;
                        return t;                           //这个返回值在addWaiter函数中没有用
                    }
                }
            }
    }
    
    
    //通过调用addWaiter函数,AQS将当前线程加入到了等待队列,但是还没有阻塞当前线程的执行,接下来我们就来分析一下acquireQueued函数.
    //AQS类的acquireQueued函数,传入参数是已经加入到队列中的新的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)) {         //如果新插入的这个节点是head节点的后面节点,说明新来的节点是将要获取锁的线程,尝试去获取锁
                        setHead(node);                          //将自己设置为头节点
                        p.next = null; // help GC               //原来的节点next置空
                        failed = false;
                        return interrupted;             //因为没有被中断所以返回false
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&    //判断是否要进入阻塞状态.如果`shouldParkAfterFailedAcquire`返回true,表示需要进入阻塞
                        parkAndCheckInterrupt())                    ////调用parkAndCheckInterrupt挂起线程,等待被唤醒             
                        interrupted = true;             
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
    }
    
    //前面我们已经说过只有前一个节点pred的线程状态为SIGNAL时,当前节点的线程才能被挂起。
     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.
                 * 前一个节点处于取消获取独占性变量的状态,所以,可以跳过去,返回false
                 */
                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.
                 */
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
     }
  • 相关阅读:
    mybatis中_parameter使用和常用sql
    ibatis中井号跟美元符号区别(#.$)
    mybatis动态sql中的trim标签的使用
    c语言捕捉异常
    lua lua解读
    lua luaconf解读
    android堆栈调试--详细
    cocos2d-x安卓应用启动调用过程简析
    ndk-stack使用方法
    cocos2dx3.2移植android
  • 原文地址:https://www.cnblogs.com/da-peng/p/10009810.html
Copyright © 2011-2022 走看看