美文网首页
java并发包之ReentrantLock

java并发包之ReentrantLock

作者: kokokokokoishi | 来源:发表于2019-01-08 00:38 被阅读0次

    在阅读本文前,需要对AQS有大概的了解

    ReentrantLock,即重入锁,表示持有资源的锁的线程可对资源进行重复加锁,其支持公平和非公平两种模式,其默认使用非公平锁。
    在ReentrantLock中,通过将加锁的操作委托给ReentrantLock$Sync来实现,Sync继承
    AbstractQueuedSynchronizer,实现了tryRelease,同时定义了nonfairTryAcquire供其非公平实现NonfairSync调用。

    ReentrantLock#lock

    public void lock() {
            sync.lock(); //委托给sync
    }
    

    我们先来看一下非公平锁

    /**
         * Sync object for non-fair locks
         */
        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
                    acquire(1);
            }
    
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
    

    在lock时,如果对statecas成功,则当前线程直接获取锁,并把自己设置位锁的持有者,否则调用acquire, AQS 使用了模板方法

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

    我们要关注的实际上是NonfairSync#tryAcquire,可以看到,这里直接调用了Sync提供的nonfairTryAcquire

     /**
             * Performs non-fair tryLock.  tryAcquire is implemented in
             * subclasses, but both need nonfair try for trylock method.
             */
            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;
            }
    

    在这里,同样如果state 为0,则尝试cas,并把自己设置为锁的持有者,若state不为0,则检查自己是否为锁的持有者,若已持有,则state加一,可以看到这里使得ReentrantLock可重入,下面我们看一下其公平实现

    static final class FairSync extends Sync {
            final void lock() {
                acquire(1);
            }
    
            /**
             * 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() &&
                        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;
            }
        }
    

    这里唯一的区别是在尝试对state进行CAS操作前加上了!hasQueuedPredecessors()
    这个判断,这是用来判断是否有线程排队在当前线程之前,若有,则当前线程进入AQS同步队列。而在非公平实现中,当前线程可越过排队的线程直接尝试获取锁。

    那么为什么默认要使用非公平锁呢,因为在非公平锁中,一个线程在释放了锁之后,再次竞争锁的时候,很可能继续获得锁,虽然有可能导致线程饥饿,但减少了上下文的切换,如果使用公平锁,则不会出现某一线程连续竞争到锁的情况,但会加大上下文切换,减小吞吐量

    相关文章

      网友评论

          本文标题:java并发包之ReentrantLock

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