美文网首页
AbstractQueuedSynchronizer- 独占锁实

AbstractQueuedSynchronizer- 独占锁实

作者: 凯玲之恋 | 来源:发表于2021-02-28 16:01 被阅读0次

    1-ReentrantLock

    ReentrantLock是独占锁,而且内部可以是公平锁,非公平锁;
    公平锁:
    公平锁:加锁钱需要检查是否还有在排队(等待)的线程,优先排队的

            final void lock() {
                acquire(1);
            }   
    
    

    非公平锁:
    加锁时无需考虑之前是否有线程等待,直接尝试获取锁,获取失败会自动追加到同步队列队尾

       final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    
    

    2 上锁流程

    2.1 acquire方法

        public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
    
        protected boolean tryAcquire(int var1) {
            throw new UnsupportedOperationException();
        }
    
    
        static void selfInterrupt() {
            Thread.currentThread().interrupt();
        }
    
    

    涉及简单方法分析:

    • tryAcquire方法直接抛出异常,也即是自定义独占锁必须实现这个方法;
    • selfInterrupt 获取锁的线程进行中断操作,这个并不一定导致线程停止

    2.2 addWaiter方法

    整体来说,就是加入一个节点到队列尾部;如果未初始化队列,则进行初始化(延时策略)

    private Node addWaiter(Node mode) {
            Node node = new Node(mode);
    
            for (;;) {
                Node oldTail = tail;
                if (oldTail != null) {
                    U.putObject(node, Node.PREV, oldTail);
                    if (compareAndSetTail(oldTail, node)) {
                        oldTail.next = node;
                        return node;
                    }
                } else {
                    initializeSyncQueue();
                }
            }
        }
    
    
    1. 首先生成一个Node节点,这个节点nextWaiter为空(Node.EXCLUSIVE为空对象);独占锁的nextWaiter为空
    2. for循环自旋
    3. 如果队列未进行初始化,则initializeSyncQueue进行初始化,如果不成功,继续此步骤直至成功
    4. 加入队列尾部

    2.3 acquireQueued方法

        final boolean acquireQueued(final Node node, int arg) {
            try {
                boolean interrupted = false;
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC
                        return interrupted;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            } catch (Throwable t) {
                cancelAcquire(node);
                throw t;
            }
        }
    
    
    • for循环自旋;如果其是头节点下的第一个节点,如果尝试获取资源成功,则进行设置为队列头,释放之前头节点,并返回false;返回false,则意味线程跳出自旋,可以继续执行
    • 如果不是锁等待队列的第二个,则执行shouldParkAfterFailedAcquire方法,如果为true,继续执行parkAndCheckInterrupt方法
    • shouldParkAfterFailedAcquire方法执行后,返回false会去掉取消的节点,之后如果未有状态变化(比如外部取消线程,打断等操作),则会返回true,可以详细看下面方法源码

    2.4 shouldParkAfterFailedAcquire方法

    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 {
                pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
            }
            return false;
        }
    
    
    • 如果node 前一个节点pred节点已经是等待唤醒状态,则返回true,表示当线线程应该被暂停
    • 如果node前一个节点状态大于0,暂时好像只有取消状态的,则找到一个状态小于等于0的,并是node为其后继节点,则寻找过程中的节点都会被移除队列,返回false
    • 如果node前一个节点已经是小于等于0了,这时把前一个几点的状态置为等待唤醒-1,返回false

    2.6 parkAndCheckInterrupt方法

       private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);
            return Thread.interrupted();
        }
    
    
    • 暂停正在执行的线程,并返回打断状态

    3 释放锁流程

    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需要自定义实现,否则直接抛出异常
    • 如果头节点不是正在运行状态,则解锁头节点线程,释放锁成功
    • 否则不需要释放锁,或者释放失败(按照正常,在锁等待队列中,独占锁/条件锁,都会为signal状态,为共享锁为signal或者PROPAGATE状态)

    3.1 unparkSuccessor方法

        private void unparkSuccessor(Node node) {
            int ws = node.waitStatus;
            if (ws < 0)
                node.compareAndSetWaitStatus(ws, 0);
            Node s = node.next;
            if (s == null || s.waitStatus > 0) {
                s = null;
                for (Node p = tail; p != node && p != null; p = p.prev)
                    if (p.waitStatus <= 0)
                        s = p;
            }
            if (s != null)
                LockSupport.unpark(s.thread);
        }
    
    
    • CAS操作,释放头节点状态设置为0
    • 从头结点后寻找一个节点不为空,且节点为0的节点,释放此节点的线程(上述获取资源时,进行自旋,去除头结点后的取消的节点后的第一个节点才可以获取资源)

    4 独占锁原理小结

    • nextWaiter为空
    • 排队等锁的队列,头优先获取资源(对于非公平锁,新获取未排队的线程也会获取锁);尝试获取资源的线程排队到队尾
    • 获取资源失败的线程,被挂起;持有线程执行完毕,则头节点的下一个节点恢复执行,尝试获取资源(非公平锁,会和新获取锁未排队进来的线程争夺锁)成功后继续执行其任务,失败线程挂起,并置为等待唤醒状态
    • 可重入锁,即当前线程再次获取资源,状态+1,释放资源状态-1,如果是0,则是当前线程完全释放了资源,其它排队线程可以获取资源了;代码如下:非公平锁的代码
    
       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;
            }
    
            protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    
    

    参考

    AbstractQueuedSynchronizer原理解析

    相关文章

      网友评论

          本文标题:AbstractQueuedSynchronizer- 独占锁实

          本文链接:https://www.haomeiwen.com/subject/igrgfltx.html