ReentrantLock

2018-05-14  本文已影响0人  囧囧有神2号

AQS是同步器的基础,要先了解AQS的实现
使用实例:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
  while(条件判断表达式) {
      condition.wait();
  }
 // 处理逻辑
} finally {
    lock.unlock();
}

ReentrantLock可重入锁就是当前持有该锁的线程能够多次获取该锁,无需等待;
我们之前介绍过AQS,它是J.U.C包的基础,实现了同步器的主要功能。那么ReentrantLock是怎么利用AQS的?
这要从ReentrantLock的一个内部类Sync的父类说起,Sync的父类是AQS,Sync的两个实现类分别是NonfairSync和FairSync,由名字可以猜到,一个是用于实现公平锁、一个是用于实现非公平锁。
那么Sync为什么要被设计成内部类呢?这是文档里建议你使用的方法,子类应该是一个非public的内部类。为什么我想是因为安全,AQS里有很多方法,直接让ReentrantLock继承意味着拥有了操作权,容易误用,所以采用内部类实现的方法;

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

ReentrantLock的lock与unlock方法委托给了sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        abstract void lock();

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

构造器:

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

非公平锁:

在上面的实例中,使用的就是非公平锁,啥叫非公平?就是插队,举个例子:A,B两个线程,B排在A后等待被唤醒,此时A执行完了同步状态清零,唤醒B,但在这是C抢了进来,B没有争过C,也就是B被C插了队,这是不公平的;
继承AQS的子类,要实现自己的tryRelease与tryAcquire;

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

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

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

举例:A,B两个线程竞争,A成功同步状态会被设置成1,exclusiveOwnerThread(在AbstractOwnableSynchronizer中);B失败。
B会调用acquire——>tryAcquire——>nonfairTryAcquire:在该方法中又会再一次判断下同步状态因为A可能执行完了,否则会检查当前线程是否是exclusiveOwnerThread中保存的线程,是则增加同步状态次数(这里是可重入的代码实现),都不是,则放入阻塞队列(逻辑在我的AQS文章中)
nonfairTryAcquire里实现了可重入的逻辑,与exclusiveOwnerThread保存的线程相等则允许进入,增加次数。

公平锁

之前说非公平是因为存在插队,而插队发生在AQS的acquireQueued方法的for循环中

for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }

之前的AQS里介绍过,当前线程release将同步状态归零,这是一处被插队的机会;当在等待队列中等待的线程被head唤醒,他会再一次进行判断,就是tryAcquire,在这里就有可能被竞争的线程抢先,即插队;所以要想实现公平锁就得对tryAcquire进行更改,怎么改?不是head的next节点就不能返回true

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

hasQueuedPredecessors就实现了这个逻辑,下一个线程首先必须是head的next指向的节点才行。

重入逻辑

在非公平锁的nonfairTryAcquire里与公平锁FairSync 的tryAcquire里都有如下重入逻辑

else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
上一篇下一篇

猜你喜欢

热点阅读