Java锁之ReentrantLock(源码详解)

  • 视频地址
  • Java学习文档

ReentrantLock 这个Java中重要的锁,我想可能很多人只是听过,并没有使用过,我在看RocketMQ客户端源码的时候发现大量的使用了这个ReentrantLock,从而引起了我的兴趣,下面我们一起从源码的角度来学习ReentrantLock。

我们先来看一下ReentrantLock的继承关系

Java锁之ReentrantLock(源码详解)_第1张图片

  1. 它实现了 Lock和Serializable接口
  2. 它有三个内部类 Sync、NonfairSync、FairSync
  3. 其中 Sync 是抽象内部类,NonfairSync、FairSync 都继承了 Sync

在正式学习源码之前,我们先来了解几个锁相关的理论

  1. 可重入锁:可重入锁的意思就是当一个线程拿到锁之后,它还可以继续加锁,而不会出现死锁,相对应的就是在释放锁的时候也要释放多次。
  2. CAS:比较并交换,这是乐观锁的一个常规实现,先对比旧的值是不是A,如果是就替换成B,如果不是就不替换。但是会出现ABA的问题,比如我们对比的时候是A,但是我们在替换的过程中,其它线程又去把A换成C,再换成A,这时候我们判断还是A就替换了,但实际上其实已经改变过了,但在大多数场景中这是没有问题了。
  3. 公平锁/非公平锁:当线程A去尝试获取锁,这时候没获取到就进入队列等待,然后B来获取锁,这时候刚好锁释放了,非公平锁代码B会直接获取到锁。但明明是A先来的呀?A却还在等待,这样会产生“饿死现象”——A可能永远也获取不到锁了。但这样的好处是不需要去激活等待的线程,当前有资源就直接用,性能更好。(不需要管等待队列的锁就是非公平锁,反之亦然

1、Sync

我们由内而外的来剖析这个 ReentrantLock,先来看看它内在的 儿子(Sync)、和孙子(NonFairSync、FairSync)

// 1.继承了 AbstractQueuedSynchronizer 
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    // 定义了一个抽象方法 lock,有子类去具体实现
    abstract void lock();

    // 非公平尝试获得锁实现
    final boolean nonfairTryAcquire(int acquires) { // ... }
    
    // 释放锁
    protected final boolean tryRelease(int releases) { // ...}
    
    // 判断当前锁持有者是不是自己
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    
    // 创建一个条件
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // 获取当前持有锁的线程
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    
    // ReentrantLock 是可重入锁,获取当前加锁的次数
    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
    }
}

上面有2个重要的方法,我在上面给注释了,下面来详细解析这两个方法 nonfairTryAcquire、tryRelease


1、nonfairTryAcquire

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

非公平尝试获取锁,它是被 final修饰,所以不能被重写,但是可以被子类使用。

  • 它的逻辑也很简单,如果当前锁没有被使用,那就使用 CAS算法 来设置值,这里调用的是底层的方法,如果设置成功说明当前可以获得锁,把当前持有锁的线程设置成当前线程。
  • 如果当前锁已经被持有了,并且持有锁的线程就是当前线程,那就让持有次数累加(ReentrantLock是可重入锁)
  • 如果不满足上述两种就返回 false。

2、tryRelease

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

它是被 protected final 修饰的方法,也就是不可以被重写,而且只能自己和子类使用。


它的逻辑也很简单,如果当前操作线程和持有锁的不是同一个就抛出异常,如果减完之后加锁标识为 0,就说明已经彻底释放了,就把当前持有锁的线程设置为 null,最后返回释放结果。


2、NonFairSync

不公平锁,一个静态内部类,它里面就2个方法

  1. lock 方法重写,如果可以CAS成功,就直接让当前线程持有锁(典型的不公平实现),否则就调用 爷爷的acquire方法。(acquire 是 AbstractQueuedSynchronizer的方法)
  2. tryAcquire 获取锁,直接调用父类的不公平加锁实现。
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);
    }
}

1、acquire

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  1. 首先调用tryAcquire 去尝试获得锁,上面我们看到非公平锁里面重写了 tryAcquire 方法,其实就是调用 Sync 的 nonfairTryAcquire 方法。
  2. 如果已经获取到锁,就结束了,如果获取不到就把当前请求加入到队列中去 acquireQueued
  3. 通知当前线程中断 selfInterrupt

3、FairSync

公平锁,一个静态内部类,它里面就2个方法

  1. Lock 直接调用爷爷的 acquire 方法,上面已经详解过了
  2. tryAcquire 尝试获得锁方法,它这里唯一不一样的就是如果当前锁没有被持有,在自己持有之前会先检查排队的队列是否有等到的,如果有则获取失败。
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;
    }
}

4、ReentrantLock

上面我们已经着重看了 ReentrantLock 的内部类了,现在我们来看看 ReentrantLock 本身。

Java锁之ReentrantLock(源码详解)_第2张图片


1、内部参数

这两个参数就没什么好说了的

private static final long serialVersionUID = 7373984872572414699L;

private final Sync sync;

2、构造方法

  1. 默认的空参构造方法创建的是 非公平锁
  2. 有参构造方法,根据传递的参数确定锁是公平还是非公平
public ReentrantLock() {
    sync = new NonfairSync();
}

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

3、普通方法


1、lock

直接调用 sync 的lock方法,公平锁和非公平锁都有自己的实现

public void lock() {
    sync.lock();
}

2、lockInterruptibly

获取锁,如果当前线程中断就抛出异常

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

3、tryLock

以非公平的方式获取锁

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

4、tryLock(long timeout, TimeUnit unit)

根据当前锁的类型(公平 or 非公平),来获取锁,并设置超时时间

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

5、unlock

释放锁

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

6、newCondition

创建一个条件

public Condition newCondition() {
    return sync.newCondition();
}

7、getHoldCount

获取当前可重入锁的加锁次数

public int getHoldCount() {
    return sync.getHoldCount();
}

final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}

8、isHeldByCurrentThread

判断当前持有锁的对象是不是自己

public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}

protected final boolean isHeldExclusively() {
    return getExclusiveOwnerThread() == Thread.currentThread();
}

9、isLocked

看当前锁,是否已经被持有了

public boolean isLocked() {
    return sync.isLocked();
}

final boolean isLocked() {
    return getState() != 0;
}

10、isFair

判断当前锁是否是公平锁

public final boolean isFair() {
    return sync instanceof FairSync;
}

11、getOwner

获取当前持有锁的线程

protected Thread getOwner() {
    return sync.getOwner();
}

12、hasQueuedThreads

判断当前是否有等待锁的

public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
}

// AbstractQueuedSynchronizer 的方法
public final boolean hasQueuedThreads() {
    return head != tail;
}

13、getQueueLength

获取等待锁的长度

public final int getQueueLength() {
    return sync.getQueueLength();
}

// AbstractQueuedSynchronizer 的方法
public final int getQueueLength() {
    int n = 0;
    for (Node p = tail; p != null; p = p.prev) {
        if (p.thread != null)
            ++n;
    }
    return n;
}

14、getQueuedThreads

获取正在等待锁的线程

protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
}

// AbstractQueuedSynchronizer 的方法
public final Collection<Thread> getQueuedThreads() {
    ArrayList<Thread> list = new ArrayList<Thread>();
    for (Node p = tail; p != null; p = p.prev) {
        Thread t = p.thread;
        if (t != null)
            list.add(t);
    }
    return list;
}

15、hasWaiters

是否有等待某种添加的锁

public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}

16、getWaitQueueLength

获取等待某种条件锁的数量

public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}

17、getWaitingThreads

获取等待某种条件锁的线程

protected Collection<Thread> getWaitingThreads(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}

18、toString

public String toString() {
    Thread o = sync.getOwner();
    return super.toString() + ((o == null) ?
                               "[Unlocked]" :
                               "[Locked by thread " + o.getName() + "]");
}

你可能感兴趣的:(源码学习,#,Java,中阶,java,锁,lock,reentrantLock,Java锁)