美文网首页
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