前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ReentrantLock 源码分析 -- ReentrantLock 的加锁与解锁

ReentrantLock 源码分析 -- ReentrantLock 的加锁与解锁

作者头像
用户3147702
发布2022-06-27 12:44:01
2660
发布2022-06-27 12:44:01
举报
文章被收录于专栏:小脑斧科技博客

1. ReentrantLock 与 AQS 独占锁

上一篇文章中,我们介绍了 ReentrantLock 的用法,他就是通过 AQS 来实现的,也是 AQS 独占模式的典型示例。 接下来我们就来看看 ReentrantLock 是如何实现的。

根据类图,我们可以看到 ReentrantLock 拥有三个内部类,分别是。 上文类图中,Sync 实现了解锁的 tryRelease 与 tryReleaseShared 方法,而其子类 NonfairSync(非公平锁) 与 FairSync(公平锁) 则实现了 tryAcquire 与 tryAcquireShared 及 isHeldExclusively 方法,从而让整个锁的实现层次更加清晰。

2. ReentrantLock 的加锁与解锁

上文中我们介绍过 ReentrantLock 的加锁与解锁方法,我们来看看他们究竟是怎么实现的。

2.1. 加锁

加锁 — lock

可中断锁 — lockInterruptibly

尝试获取锁 — tryLock

代码语言:javascript
复制
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

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

2.2. 解锁 — unlock

代码语言:javascript
复制
public void unlock() {
    sync.release(1);
}

可以看到,所有的加锁解锁方法均直接调用了内部类 Sync。

3. Sync 与他的两个子类

根据类图,我们可以看到,ReentrantLock 拥有一个内部类 Sync,他是 AQS 的子类,同时,ReentrantLock 还拥有另外两个内部类 — FairSync 和 NonfairSync,他们分别提供了公平锁与非公平锁的加锁方法。

4. 公平锁加锁方法 — FairSync.lock 方法

公平锁是通过 Sync 的子类 FairSync 实现的,他复写了父类的 abstract lock 方法。

代码语言:javascript
复制
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;
    }
}

lock 方法直接调用了 AQS 的 acquire 方法,这个方法在之前的文章中已经介绍过了,这里不再赘述。 同时,他实现了 Sync 没有实现的 tryAcquire 方法,这个方法就是实际上的公平锁的加锁方法。 1. 此前我们看到,AQS 中有一个 state 成员,这个 int 类型的成员就是锁状态的标识,为 0 则表示当前锁处于空闲状态,否则说明锁处于非空闲状态 2. 同时我们提到过,AbstractQueuedSynchronizer 继承自 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 中 exclusiveOwnerThread 字段记录了当前正在占用锁的线程 了解了上述两点,公平锁的加锁代码就非常简单了。

通过 AbstractQueuedSynchronizer 提供的原子操作 compareAndSetState 实现了加锁的并发控制,当然他最终调用的是 UNSAFE 类的成员方法:

代码语言:javascript
复制
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

5. 非公平锁的加锁方法 — NonfairSync.lock

NonfairSync 类提供了非公平锁的加锁方法。

代码语言:javascript
复制
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);
    }
}

可以看到,非公平锁的加锁与公平锁的加锁有一点不同,那就是如果当前锁状态是空闲状态,那么就可以直接获取锁,而公平锁显然是不能这么做的,因为公平锁必须要先判断 AQS 同步队列中是否有先入队的正在等待获取锁的线程,否则直接获取就打破了公平锁的原则了。 同样的,真正的加锁方法仍然是 Sync 所没有实现的 tryAcquire 方法,NonfairSync 中直接调用了父类的 nonfairTryAcquire 方法,也就是说,虽然 Sync 并没有直接实现 tryAcquire 方法,但他其实已经提供了一个非公平锁的加锁实现,那就是 nonfairTryAcquire 方法,我们来看看 Sync 的 nonfairTryAcquire 方法做了什么。

5.1. 非公平锁的加锁 — Sync.nonfairTryAcquire

代码语言:javascript
复制
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;
}

乍一看,这个方法和我们上面介绍过的公平锁加锁方法似乎没什么区别,仔细一看,还是我们上面讲到的,非公平锁与公平锁最大的区别在于,他在获取锁之前不需要先去判断 AQS 同步队列中是否有正在等待获取锁的节点,这样,非公平锁的实现看起来就更简单了。

6. 解锁 — Sync.tryRelease

代码语言:javascript
复制
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;
}

这段解锁的代码看上去非常简单,既然加锁的时候对 AQS 的状态值进行了增加操作,那么在解锁的时候就必须进行逆向的减操作了,同时,如果此时锁已经被释放,那么就需要把 AbstractOwnableSynchronizer 类中的 exclusiveOwnerThread 成员置空。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小脑斧科技博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. ReentrantLock 与 AQS 独占锁
  • 2. ReentrantLock 的加锁与解锁
    • 2.1. 加锁
      • 2.2. 解锁 — unlock
      • 3. Sync 与他的两个子类
      • 4. 公平锁加锁方法 — FairSync.lock 方法
      • 5. 非公平锁的加锁方法 — NonfairSync.lock
        • 5.1. 非公平锁的加锁 — Sync.nonfairTryAcquire
        • 6. 解锁 — Sync.tryRelease
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档