聊聊公平锁和非公平锁

Posted 魔法二少

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊公平锁和非公平锁相关的知识,希望对你有一定的参考价值。

AQS中公平锁和非公平锁怎么实现的?

avatar
如上图所示,公平锁和非公平锁的代码主要有两处地方有区别:

  • 公平锁调用lock()方法获取锁时,直接抢占锁【acquire(1),注释1】, 非公平锁调用lock()方法获取锁时,先执行cas获取锁,获取失败后再抢占锁【acquire(1),注释2】;
  • 调用acquire(1)抢占锁时,若锁没有被占用(state=0)
    • 非公平锁则直接执行cas代码尝试获取锁(注释3);
    • 公平锁则先检查AQS队列,若AQS队列为空或AQS头节点的后继节点是当前节点(注释4的hasQueuedPredecessors()方法),则再执行cas代码尝试获取锁(注释5);

非公平锁:

 //非公平锁
 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))  //注释2,调用lock()后先执行cas试图获取锁
                setExclusiveOwnerThread(Thread.currentThread());
            else  
                acquire(1); //获取失败后再抢占锁
        }
        
        //非公平锁和公平锁都会执行该方法
        public final void acquire(int arg) {
        if (!tryAcquire(arg) &&   
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);  //锁没有被占用或锁可重入则返回true
        }
        
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {  //锁没有被占用
                if (compareAndSetState(0, acquires)) {  //注释3,执行cas
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {  
        //已获得锁的线程就是当前线程【getExclusiveOwnerThread()获取当前占用锁的线程】,则重入(ReentrantLock是可重入锁)   
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
}

公平锁:

    
//公平锁
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);//注释1,调用lock()后直接抢占锁
    }
    
    //非公平锁和公平锁都会执行该方法
        public final void acquire(int arg) {
        if (!tryAcquire(arg) &&   
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
        }

    /**
     * Fair version of tryAcquire.  Don\'t grant access unless
     * recursive call or no waiters or is first.
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&  //hasQueuedPredecessors返回false后可继续执行compareAndSetState=cas
                compareAndSetState(0, acquires)) {  //注释5
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
        //已获得锁的线程就是当前线程【getExclusiveOwnerThread()获取占用锁的线程】,则重入拿锁(ReentrantLock是可重入锁)
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    
    //注释4:若AQS队列为空或AQS头节点的后继节点是当前节点,返回false
    public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
  • ReentrantLock、Lock、FairSync、NonfairSync、Sync、AbstractQueuedSynchronizer之间的关系
    ReentrantLock继承Lock,并拥有内部类FairSync、NonfairSync、Sync;FairSync、NonfairSync继承Sync;Sync继承AbstractQueuedSynchronizer之间的关系

avatar

以上是关于聊聊公平锁和非公平锁的主要内容,如果未能解决你的问题,请参考以下文章

敖丙哥哥,跟我聊一下公平锁和非公平锁嘛?

敖丙哥哥,跟我聊一下公平锁和非公平锁嘛?

多线程学习 公平锁和非公平锁

并发编程 —— 源码分析公平锁和非公平锁

Java中的公平锁和非公平锁实现详解

面试必备|Java中的公平锁和非公平锁,可重入锁,自旋锁