JUC-ReentrantLock锁源码分析

ReentrantLock锁用法可以参考JUC-ReentrantLock锁基础篇
AQS用法可以参考JUC-AQS原理篇
AQS源码解析可以参考JUC-AQS源码篇
AQS之Condition源码解析可以参考JUC-AQS的Condition之await和signal源码解析

建议阅读ReentrantLock锁源码时,先去阅读JUC-ReentrantLock锁基础篇,涉及到的AQS源码可以参考JUC-AQS源码篇

文章目录

  • 1.ReentrantLock类结构图
    • 1.1ReentrantLock的构造方法
  • 2.lock()方法源码
    • 2.1 公平锁时抽象方法lock()的子类实现源码
    • 2.2 非公平锁时抽象方法lock()的子类实现源码
  • 3.unlock()方法源码

1.ReentrantLock类结构图

JUC-ReentrantLock锁源码分析_第1张图片
ReentrantLock内部有一个继承AbstractQueuedSynchronizer的静态抽象类Sync,NonfairSync和FairSync继承了Sync并实现了它的抽象方法lock()。
在ReentrantLock中,AQS有tryAcquire(int arg),tryRelease(int arg)两个方法需要子类来实现。而子类Sync只实现了tryRelease方法,tryAcquire由Sync的子类NonfairSync和FairSync来实现。而Sync自己也有一个抽象方法方法lock()需要它的子类来实现。
总的来说就是
Sync实现了AQS的抽象方法tryRelease。
NonfairSync实现了AQS的抽象方法tryAcquire和Sync的抽象方法lock
FairSync也实现了AQS的抽象方法tryAcquire和Sync的抽象方法lock

1.1ReentrantLock的构造方法

 private final Sync sync;

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

由上面可以看出通过参数fair可以控制是创造一个公平的锁ReentrantLock还是一个非公平的锁ReentrantLock。什么是公平锁什么是非公平锁可以参考上面的JUC-ReentrantLock锁基础篇中的公平锁,非公平锁的讲解。
ReentrantLock加锁的方法调用大致过程是:

reentrantLock类的lock()调用—>Sync子类实现的lock()调用—>AQS类的acquire(1)调用—>Sync子类实现的tryAcquire(1)

ReentrantLock解锁的方法调用大致过程是:

reentrantLock类的unlock()调用—>AQS类的release(1)—>Sync类的tryRelease(1)

在ReentrantLock中AQS的成员变量state取值的含义:

  • state值等于0,代表这把锁目前没有被其他线程获取。
  • state值大于0,代表已经有线程获取到了这把锁。

2.lock()方法源码

我们知道ReentrantLock获取锁调用的入口方法是reentrantLock.lock()方法。

//此lock方法是ReentrantLock类里面的
public void lock() {
        //如果是公平锁的话,sync.lock()方法调用的是FairSync类里面的lock方法。
        //如果是非公平锁的话,sync.lock()方法调用的是NonfairSync类里面的lock方法。
        sync.lock();
    }

抽象类Sync的lock()抽象方法是由它的两个子类FairSync和NonfairSync来实现的。
而成员变量sync引用究竟是指向抽象类Sync的那个子类,则是根据ReentrantLock的构造函数决定的

  1. fair=true,以公平锁策略构造,sync=new FairSync();
  2. fair=false,以非公平锁策略构造,sync=new NonfairSync();

2.1 公平锁时抽象方法lock()的子类实现源码

//此lock方法是FairSync类里面的
 final void lock() {
            acquire(1);
        }

lock()方法直接调用父类AQS(AbstractQueuedSynchronizer)里面的acquire方法。

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

acquire方法的作用就是通过调用tryAcquire方法判断能否获取锁,不能的话就将当前线程加入同步队列中并阻塞。
AQS里面的acquire方法会先调用tryAcquire方法判断能否获取锁,其他的方法,如acquireQueued,addWaiter,selfInterrupt都是属于AQS的内部方法,可以参考JUC-AQS源码篇进行阅读。而tryAcquire方法是由AQS的子类来进行实现的。
在公平锁的情况下,tryAcquire方法是由FairSync类来进行实现的。其源码如下:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取state值
            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;
        }

此方法返回true的话,代表获取了锁,返回false的话就代表获取锁失败。
大致逻辑就是

  1. 先获取state的值。
  2. 如果state值等于0的话,代表这把锁没有被其他线程获取,注意因为这是一把公平锁,所以不能直接调用compareAndSetState(0, acquires)方法用CAS的方式来获取锁,我们得用hasQueuedPredecessors方法(AQS之hasQueuedPredecessors方法源码分析)来判断一下,是否已经有线程在等着获取这把锁,如果有,那我们就不能去跟它去抢这把锁(这就体现了公平,先来先到,既然你比我早,那么这把锁你先获取)。直接返回false。如果没有,那么我就调用compareAndSetState(0, acquires)来竞争这把锁,竞争到了,调用setExclusiveOwnerThread方法,将当前线程值赋值给exclusiveOwnerThread变量(这样做的原因是为了实现ReentrantLock的可重入性),然后返回true。没有竞争到,就返回false。
  3. 如果state值不等于0的,代表这把锁已经被其他线程获取了。但是我们不能直接返回false。因为ReentrantLock具有可重入性。我们得通过current == getExclusiveOwnerThread()来判断已经获取这把锁的线程跟当前线程是不是同一个,如果是的话,那这把锁可以被此线程再次获取,只不过将state值加1,然后返回true。如果不是就返回false。
    注意:为什么当前线程已经获取锁了,当它再重复的获取这把锁时,state值为啥要加1?因为,当一个线程第一次获取这把锁时,state值为1,第二次重复获取这把锁时,如果state值不加1变成2的话,那么当它执行第一次释放锁的动作时,state值要-1,这样的话,state值就变成了0,但是,这个线程还是获取了这把锁(第二次获取的),这就不对了。

2.2 非公平锁时抽象方法lock()的子类实现源码

//此lock方法是NonfairSync类里面的
final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

此方法是先调用compareAndSetState通过CAS的方法看能不能获取到锁,如果获取不到的话,就再调用父类AQS(AbstractQueuedSynchronizer)的acquire方法。从这就可以看出非公平性出来了,当前线程不管前面还有没有等着获取锁的线程,它先抢锁,抢到了就是它的了。

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

acquire方法的作用就是通过调用tryAcquire方法判断能否获取锁,不能的话就将当前线程加入同步队列中并阻塞。
AQS里面的acquire方法会先调用tryAcquire方法判断能否获取锁,其他的方法,如acquireQueued,addWaiter,selfInterrupt都是属于AQS的内部方法,可以参考JUC-AQS源码篇进行阅读。而tryAcquire方法是由AQS的子类来进行实现的。
在非公平锁的情况下,tryAcquire方法是由NonfairSync类来进行实现的。其源码如下:

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

NonfairSync的tryAcquire方法是直接调用了父类Sync的nonfairTryAcquire方法:

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取state变量
            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;
        }

此方法返回true的话,代表获取了锁,返回false的话就代表获取锁失败。
大致逻辑就是

  1. 先获取state的值。
  2. 如果state值等于0的话,代表这把锁没有被其他线程获取,注意因为这是一把非公平锁,我们直接就调用compareAndSetState(0, acquires)来竞争这把锁,竞争到了,调用setExclusiveOwnerThread方法,将当前线程值赋值给exclusiveOwnerThread变量(这样做的原因是为了实现ReentrantLock的可重入性),然后返回true。没有竞争到,就返回false。
  3. 如果state值不等于0的,代表这把锁已经被其他线程获取了。但是我们不能直接返回false。因为ReentrantLock具有可重入性。我们得通过current == getExclusiveOwnerThread()来判断已经获取这把锁的线程跟当前线程是不是同一个,如果是的话,那这把锁可以被此线程再次获取,只不过将state值加1,然后返回true。如果不是就返回false。

3.unlock()方法源码

我们知道ReentrantLock释放锁调用的入口方法是reentrantLock.unlock()方法。

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

release方法通过调用tryRelease方法来尝试释放当前的锁,如果释放成功的话,就唤醒head节点的后继节点。tryRelease方法是由AQS的子类Sync类来实现的。

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

tryRelease方法的大致逻辑是:

  1. 先获取state值然后将state值减1,赋值给c
  2. 然后判断当前线程跟获取锁的线程是不是同一个,因为只有获取锁的线程才能释放这把锁。如果不是同一个,抛出异常。如果是的话接着往下走。
  3. 判断c的值是不是0,如果是的话,说明已经没有线程持有这把锁了。将state值赋值为c,并调用setExclusiveOwnerThread方法将exclusiveOwnerThread变量赋值为null,返回true。
  4. 如果c的值不是0,那说明当前线程还持有这把锁(当前线程重复获取了这把锁导致的),将state值赋值为c,返回false。

你可能感兴趣的:(JUC,java,java,JUC,多线程)