前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AQS --- 渐入佳境

AQS --- 渐入佳境

作者头像
贪挽懒月
发布2021-07-23 16:16:02
2510
发布2021-07-23 16:16:02
举报
文章被收录于专栏:JavaEEJavaEE

上一讲了解了 AQS 是什么,接下来看看它到底是怎样的结构。

一. 工作原理

AQS 使用一个 volatile 的 int 类型的成员变量来表示同步状态,通过内置的 FIFO 队列来完成资源获取和排队工作,将每条要去抢占资源的线程封装成一个 node 节点来实现锁的分配,通过 CAS 来完成对 state 值的修改。

HashMap 进行 put 的时候,也不是直接存储 key value 键值对,而是将 key value 键值对封装成 Node 节点,然后用数组 + 链表 + 红黑树存储 Node。AQS 也类似,将要抢占资源的 Thread 封装成 Node节点。

二. 相关源码:

代码语言:javascript
复制
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
   
    static final class Node {
       ……
       volatile int waitStatus;
       volatile Node prev;
       volatile Node next;
       volatile Thread thread;
       ……
    }

    private transient volatile Node head;
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;
}

看到这个是不是就清清楚楚明明白白真真切切了。首先 AQS 外层是 state + CLH 队列,state 表示同步的状态,默认是0,为0时表示可以获取锁,不为0时,线程就得老老实实到队列中排队去;CLH 队列就是一个有头结点和尾结点的双端队列,如下图:

代码语言:javascript
复制
           +------+  prev +-----+       +-----+
      head |      | <---- |     | <---- |     |  tail
           +------+       +-----+       +-----+

AQS 的内层是一个 Node内部类,这个 Node 类主要有两个指针 prev 和 next、一个 waitStatus 表示线程的状、,一个 Thread 类型的变量保存等待的线程。

三. 从 ReentrantLock 看 AQS:

之前说了 AQS 是 JUC 并发包的基石,那就从我们接触最多的 ReentrantLock 入手,揭开它的神秘面纱。

先来看看 ReentrantLock 的结构图:

结构图

首先它实现了 Lock 接口,其内部主要是一个 Sync 内部类,这个内部类又有两个子类,一个 FairSync 和一个 NonfairSync,分别用来实现公平锁和非公平锁。而这个 Sync 内部类,又是 AbstractQueuedSynchronizer 的子类。

1. 我们 new ReentrantLock 的时候做了什么事?

代码语言:javascript
复制
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
 public ReentrantLock() {
     sync = new NonfairSync();
 }

通过这个构造方法可以知道,实际上是构建了一个非公平锁。如果 new 的时候传了 true,调用的构造方法就是:

代码语言:javascript
复制
/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
 public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
 }

所以传的是 true,构建的就是公平锁。

2. 公平和非公平有什么区别?

非公平锁源码:

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

公平锁源码:

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

乍一看两段代码好像没啥不一样,其实不同之处在,if (c == 0)这段判断中。公平锁多了一个判断条件,即!hasQueuedPredecessors(),看看这个方法的源码:

代码语言:javascript
复制
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这个方法也很简单,首先是头节点不等于尾节点,然后就是头节点的下一个节点为空或者头节点的下一个节点保存的 Thread 不等于当前的 Thread。简单地说就是看队列中有没有除了当前 Thread 以为的 Thread 在等待获取锁,有就返回 true,否则返回 false。所以公平锁就是多了这个判断,其他都一样。

下一篇文章将会从源码层面分析 ReentrantLock 的加锁过程,敬请期待!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 工作原理
  • 二. 相关源码:
  • 三. 从 ReentrantLock 看 AQS:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档