美文网首页
ReentrantLock

ReentrantLock

作者: 王侦 | 来源:发表于2019-07-15 08:19 被阅读0次

    1.整体调用结构

    默认情况下是非公平锁:

       public ReentrantLock() {
            sync = new NonfairSync();
        }
    
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    对于加锁、解锁、可中断锁、超时加锁、抢占非阻塞式加锁(tryLock(),即使在公平模式下也是抢占式而非排队),最终都转交给了sync来处理。

        public void lock() {
            sync.lock();
        }
    
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }
    
        //该加锁为抢占式加锁,在公平模式下也是抢占式,其不会阻塞
        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
    
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
    
        public void unlock() {
            sync.release(1);
        }
    
        public Condition newCondition() {
            return sync.newCondition();
        }
    

    2.可重入是怎么实现的?

    2.1 先看看非公平锁的lock

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

    acquire(1)调用的是AQS的:

        public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    

    其中tryAcquire(arg)调用的是NonfairSync的:

    protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    

    nonfairTryAcquire(acquires)是其父类Sync中的final方法:

            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;
            }
    

    关键就在该方法里面,current == getExclusiveOwnerThread()时不会阻塞,而是会记录当前线程加锁的次数并更新到state中去。

    2.2 再看看公平锁

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

    与非公平锁一样调用AQS的acquire,然后acquire会调用FairSync.tryAcquire:

            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    

    同理,有一模一样的处理方式:current == getExclusiveOwnerThread()时会更新该线程加锁的次数。

    对于其他的中断锁、超时锁等,都会调用公平锁、非公平锁的tryAcquire(其中断机制和超时机制由AQS已经完全实现了),而tryAcquire中加锁时都会处理可重入的实现。

    2.3 释放锁

        public void unlock() {
            sync.release(1);
        }
    

    调用AQS的release:

        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    

    AQS的release会调用Sync.tryRelease final方法:

            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;
            }
    

    可以看出如果线程加锁了n次,必须解锁n次才能真正释放锁。

    3.公平锁与非公平锁的区别

    从上面可以看出释放锁时公平锁和非公平锁都一样,因此公平和非公平是针对获取锁而言的,具体地讲,是tryAcquire的不同。

    非公平锁的tryAcquire:

            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    
            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;
            }
    

    非公平锁,会有一个CAS抢占过程,如果抢占成功,加成功获取锁,而非按照FIFO模式获取锁。

    公平锁的tryAcquire:

            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    
        /**
         * Queries whether any threads have been waiting to acquire longer
         * than the current thread.
    
         * @return {@code true} if there is a queued thread preceding the
         *         current thread, and {@code false} if the current thread
         *         is at the head of the queue or the queue is empty
         * @since 1.7
         */
    
        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());
        }
    

    如果队列中有线程在等待,则不会获取锁,也即会加入到同步队列中进行等待。

    公平锁保证了锁获取按照FIFO原则,代价是进行大量的线程切换。非公平锁虽然造成线程饥饿,但极少的线程切换(刚释放锁的线程再次获得锁的几率非常大),保证了其更大的吞吐量。

    相关文章

      网友评论

          本文标题:ReentrantLock

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