ReentrantLock原理 - 969251639/study GitHub Wiki

ReentrantLock是jdk实现的重入锁,是AQS的经典实现之一,所以必须得懂AQS
https://github.com/969251639/study/wiki/AbstractQueueSysnchorized%E5%8E%9F%E7%90%86

ReentrantLock有两种锁,通过构造方法实现

    public ReentrantLock() {
        sync = new NonfairSync();
    }

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

第一个构造函数是直接new了一个NonfairSync,表示创建的锁时非公平锁
第二个构造函数是根据参数fair来创建公平锁或非公平锁

  1. 非公平锁
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        final void lock() {//锁
            if (compareAndSetState(0, 1))//尝试cas修改state,修改成功则抢锁成功
                setExclusiveOwnerThread(Thread.currentThread());//设置当前持有锁的线程
            else//cas修改state不成功,则去竞争锁
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {//尝试去获取锁
            return nonfairTryAcquire(acquires);
        }
    }
  1. 公平锁
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {//排队取锁
            acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {//尝试获取锁
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {//
                if (!hasQueuedPredecessors() &&//判断是否线程排队获取锁,如果有则跳到下一个重入判断,否则则表示还没有线程去竞争锁,尝试CAS修改下state
                    compareAndSetState(0, acquires)) {//尝试修改下state
                    setExclusiveOwnerThread(current);//设置当前线程持有锁
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//判断是否能重入,也就是持有锁的线程是不是当前线程
                int nextc = c + acquires;//重入次数+1
                if (nextc < 0)//边界判断
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//更新state值,即重入值
                return true;
            }
            return false;
        }
    }

不管是公平锁还是非公平锁都继承自Sync

  1. Sync
    Sync是ReentrantLock的内部类,用于对接AQS的底层实现,即继承AQS,重写它的几个模板方法
    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();//获取state值
            if (c == 0) {//还没有线程获取锁
                if (compareAndSetState(0, acquires)) {//尝试cas修改state,修改成功则抢锁成功
                    setExclusiveOwnerThread(current);//设置当前持有锁的线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//判断是否能重入,也就是持有锁的线程是不是当前线程
                int nextc = c + acquires;//重入次数+1
                if (nextc < 0) // overflow 边界判断
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//更新state值,即重入值
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {//尝试释放锁
            int c = getState() - releases;//state减1(独占锁,releases=1)
            if (Thread.currentThread() != getExclusiveOwnerThread())//不是持有锁的线程释放锁,抛出异常
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {//state减到0,表示锁释放成功,否则表示重入次数减1,但还没有完全释放锁
                free = true;//释放锁成功标记
                setExclusiveOwnerThread(null);//设置持有锁的线程为null
            }
            setState(c);//更新state值
            return free;
        }

        protected final boolean isHeldExclusively() {//判断持有锁的线程是否是当前线程
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {//创建一个condition
            return new ConditionObject();
        }

        final Thread getOwner() {//获取持有锁的线程,没有则返回null
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {//获取重入次数
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {//锁是否已经被占领
            return getState() != 0;
        }

        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {//序列化用
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
  1. 公平锁和非公平锁的区别
    非公平锁在lock和tryLock的时候都会去尝试竞争锁,而不用进入到CLH队列,而公平锁则如果CLH队列不为空,则必须排队等待锁释放,所以非公平锁的吞吐量会大于公平锁,但缺点是有可能造成CLH后面的线程饥饿

  2. 其他方法
    其他方法都是围绕上面创建的NonfairSync或FairSync来实现,来逐一看下:

    public void lock() {//调用NonfairSync或FairSync竞争锁,成功返回,否则等待
        sync.lock();
    }
    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();//创建一个条件等待队列
    }
    public int getHoldCount() {
        return sync.getHoldCount();//获取锁的重入次数
    }
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();//判断当前线程是否持有锁
    }
    public boolean isLocked() {
        return sync.isLocked();//是否有线程获得锁
    }
    public final boolean isFair() {
        return sync instanceof FairSync;//锁的实现是否是公平锁
    }
    protected Thread getOwner() {
        return sync.getOwner();//获取持有锁的线程
    }
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();//是否有线程在队列等待获取锁
    }
    public final int getQueueLength() {
        return sync.getQueueLength();//获取等待队列的线程数,即有多少个线程在等待获取锁
    }