AQS源码探究_02 AQS简介及属性分析

不念不忘少年蓝@ 2023-01-18 09:16 43阅读 0赞

1. 简介

  • AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。
  • AQS是基于FIFO的队列实现的,并且内部维护了一个状态变量state,通过原子更新这个状态变量state即可以实现加锁解锁操作。

2. 主要内部类Node

  1. static final class Node {
  2. // 标识一个节点是共享模式
  3. static final Node SHARED = new Node();
  4. // 标识一个节点是互斥模式
  5. static final Node EXCLUSIVE = null;
  6. // 标识线程已取消(表示当前节点处于取消状态)
  7. static final int CANCELLED = 1;
  8. // 标识后继节点需要唤醒(表示当前节点需要唤醒他的后继节点)
  9. static final int SIGNAL = -1;
  10. // 标识线程等待在一个条件上
  11. static final int CONDITION = -2;
  12. // 标识后面的共享锁需要无条件的传播(共享锁需要连续唤醒读的线程)
  13. static final int PROPAGATE = -3;
  14. // 当前节点保存的线程对应的等待状态(node状态可选值:0,SIGNAL,CANCELLED,CONDITION,PROPAGATE)
  15. // waitStatus == 0 默认状态
  16. // waitStatus > 0 取消状态
  17. // waitStatus == -1 表示当前node如果是head节点时,释放锁之后需要唤醒它的后继节点
  18. volatile int waitStatus;
  19. // 前一个节点(前驱):用于node构建 FIFO队列~
  20. volatile Node prev;
  21. // 后一个节点(后驱):用于node构建 FIFO队列~
  22. volatile Node next;
  23. // 当前node节点封装的线程
  24. volatile Thread thread;
  25. // 下一个等待在条件上的节点(Condition锁时使用)
  26. Node nextWaiter;
  27. // 是否是共享模式
  28. final boolean isShared() {
  29. return nextWaiter == SHARED;
  30. }
  31. // 获取前一个节点
  32. final Node predecessor() throws NullPointerException {
  33. Node p = prev;
  34. if (p == null)
  35. throw new NullPointerException();
  36. else
  37. return p;
  38. }
  39. // 节点的构造方法
  40. Node() { // Used to establish initial head or SHARED marker
  41. }
  42. // 节点的构造方法
  43. Node(Thread thread, Node mode) { // Used by addWaiter
  44. // 把共享模式还是互斥模式存储到nextWaiter这个字段里面了
  45. this.nextWaiter = mode;
  46. this.thread = thread;
  47. }
  48. // 节点的构造方法
  49. Node(Thread thread, int waitStatus) { // Used by Condition
  50. // 等待的状态,在Condition中使用
  51. this.waitStatus = waitStatus;
  52. this.thread = thread;
  53. }
  54. }

双链表结构,节点中保存着当前线程、前一个节点、后一个节点以及线程的状态等信息。

3. 主要属性

  1. // 队列的头节点: 任何时刻,头结点对应的线程就是当前持锁线程~
  2. private transient volatile Node head;
  3. // 队列的尾节点:(阻塞队列不包含头结点head,是从head.next 开始,到 tail 结束,这个区间是阻塞队列~)
  4. private transient volatile Node tail;
  5. // 控制加锁解锁的状态变量
  6. // 独占模式下:0 表示未加锁状态, >0 表示已加锁状态
  7. private volatile int state;

定义了一个状态变量和一个队列,状态变量用来控制加锁解锁,队列用来放置等待的线程。

注意:这几个变量都要使用volatile关键字来修饰,因为是在多线程环境下操作,要保证它们的值修改之后对其它线程立即可见。

这几个变量的修改是直接使用的Unsafe这个类来操作的:

  1. // 获取Unsafe类的实例,注意这种方式仅限于jdk自己使用,普通用户是无法这样调用的
  2. private static final Unsafe unsafe = Unsafe.getUnsafe();
  3. // 状态变量state的偏移量
  4. private static final long stateOffset;
  5. // 头节点的偏移量
  6. private static final long headOffset;
  7. // 尾节点的偏移量
  8. private static final long tailOffset;
  9. // 等待状态的偏移量(Node的属性)
  10. private static final long waitStatusOffset;
  11. // 下一个节点的偏移量(Node的属性)
  12. private static final long nextOffset;
  13. static {
  14. try {
  15. // 获取state的偏移量
  16. stateOffset = unsafe.objectFieldOffset
  17. (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  18. // 获取head的偏移量
  19. headOffset = unsafe.objectFieldOffset
  20. (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  21. // 获取tail的偏移量
  22. tailOffset = unsafe.objectFieldOffset
  23. (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  24. // 获取waitStatus的偏移量
  25. waitStatusOffset = unsafe.objectFieldOffset
  26. (Node.class.getDeclaredField("waitStatus"));
  27. // 获取next的偏移量
  28. nextOffset = unsafe.objectFieldOffset
  29. (Node.class.getDeclaredField("next"));
  30. } catch (Exception ex) { throw new Error(ex); }
  31. }
  32. // 调用Unsafe的方法原子更新state
  33. protected final boolean compareAndSetState(int expect, int update) {
  34. return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  35. }

4. 父类属性

AQS还用到了其父类AbstractOwnableSynchronizer的一些属性:

  1. // 独占模式下:表示当前持有锁的线程~
  2. private transient Thread exclusiveOwnerThread;

发表评论

表情:
评论列表 (有 0 条评论,43人围观)

还没有评论,来说两句吧...

相关阅读

    相关 分析:AQS

    在开始这篇源码之前,最好先看下转载整理的[这篇文章][Link 1],有很多值得学习的地方。AQS是用来构建锁或者其他同步组件的基础框架。总体来说,它使用一个 int 成员变量

    相关 并发-AQS分析

    微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路 一、概述   谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈