AQS理解之五—并发编程中AQS的理解

首先看下uml类图:

AbstractOwnableSynchronizer 这个类定义是提供一个创建锁的基础,设置一个排它线程,帮助控制和监控访问。

先看下AbstractQueuedSynchronizer 这个类的内部变量。

Head和tail是两个Node变量。
int型的state。
long型的spinForTimeoutThreshold(自旋超时阈值,为固定1000 nanoseconds)
一个Unsafe类和5个unsafe偏移量

先看看Node类。

可以看源码和uml图看出,Node节点组成双向链表(prev和next),持有一个线程thread,有一个waitStatus,可能会有5种状态(SIGNAL,CANCELLED,CONDITION,PROPAGATE,初始值0),提供了2个方法,是否共享和返回非空前置节点。

static final class Node {/** Marker to indicate a node is waiting in shared mode */static final Node SHARED = new Node();/** Marker to indicate a node is waiting in exclusive mode */static final Node EXCLUSIVE = null;/** waitStatus value to indicate thread has cancelled */static final int CANCELLED =  1;/** waitStatus value to indicate successor's thread needs unparking */static final int SIGNAL    = -1;/** waitStatus value to indicate thread is waiting on condition */static final int CONDITION = -2;/*** waitStatus value to indicate the next acquireShared should* unconditionally propagate*/static final int PROPAGATE = -3;/*** Status field, taking on only the values:*   SIGNAL:     The successor of this node is (or will soon be)*               blocked (via park), so the current node must*               unpark its successor when it releases or*               cancels. To avoid races, acquire methods must*               first indicate they need a signal,*               then retry the atomic acquire, and then,*               on failure, block.*   CANCELLED:  This node is cancelled due to timeout or interrupt.*               Nodes never leave this state. In particular,*               a thread with cancelled node never again blocks.*   CONDITION:  This node is currently on a condition queue.*               It will not be used as a sync queue node*               until transferred, at which time the status*               will be set to 0. (Use of this value here has*               nothing to do with the other uses of the*               field, but simplifies mechanics.)*   PROPAGATE:  A releaseShared should be propagated to other*               nodes. This is set (for head node only) in*               doReleaseShared to ensure propagation*               continues, even if other operations have*               since intervened.*   0:          None of the above** The values are arranged numerically to simplify use.* Non-negative values mean that a node doesn't need to* signal. So, most code doesn't need to check for particular* values, just for sign.** The field is initialized to 0 for normal sync nodes, and* CONDITION for condition nodes.  It is modified using CAS* (or when possible, unconditional volatile writes).*/volatile int waitStatus;/*** Link to predecessor node that current node/thread relies on* for checking waitStatus. Assigned during enqueuing, and nulled* out (for sake of GC) only upon dequeuing.  Also, upon* cancellation of a predecessor, we short-circuit while* finding a non-cancelled one, which will always exist* because the head node is never cancelled: A node becomes* head only as a result of successful acquire. A* cancelled thread never succeeds in acquiring, and a thread only* cancels itself, not any other node.*/volatile Node prev;/*** Link to the successor node that the current node/thread* unparks upon release. Assigned during enqueuing, adjusted* when bypassing cancelled predecessors, and nulled out (for* sake of GC) when dequeued.  The enq operation does not* assign next field of a predecessor until after attachment,* so seeing a null next field does not necessarily mean that* node is at end of queue. However, if a next field appears* to be null, we can scan prev's from the tail to* double-check.  The next field of cancelled nodes is set to* point to the node itself instead of null, to make life* easier for isOnSyncQueue.*/volatile Node next;/*** The thread that enqueued this node.  Initialized on* construction and nulled out after use.*/volatile Thread thread;/*** Link to next node waiting on condition, or the special* value SHARED.  Because condition queues are accessed only* when holding in exclusive mode, we just need a simple* linked queue to hold nodes while they are waiting on* conditions. They are then transferred to the queue to* re-acquire. And because conditions can only be exclusive,* we save a field by using special value to indicate shared* mode.*/Node nextWaiter;/*** Returns true if node is waiting in shared mode.*/final boolean isShared() {return nextWaiter == SHARED;}/*** Returns previous node, or throws NullPointerException if null.* Use when predecessor cannot be null.  The null check could* be elided, but is present to help the VM.** @return the predecessor of this node*/final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}Node() {    // Used to establish initial head or SHARED marker}Node(Thread thread, Node mode) {     // Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;}
}

几个比较重要的方法getState() ,compareAndSetState,setExclusiveOwnerThread,setState(nextc)。

分别看下其中的实现:

state是一个可以用作记录状态的volatile参数,用作锁的时候,用他来记录一个线程进入锁的次数。如果state = 0时,则说明当前线程释放了锁,其他线程可以获取。

getExclusiveOwnerThread 这个获取到当前操作的线程,如果不为null,且不相等,则不能获取到锁。

Node是一个链表,记录着阻塞等待的线程,如果要为公平版本,可以按顺序在Node中取,如果不是,则不用判断是否下一个是否当前线程,谁抢占到谁执行。

waitStatus记录的是当前某个Node节点的状态。

Node节点之前我们几节看的都是用的排他锁,Exclusive的版本,AQS中还定义了一个Shared来表示共享版本。

总结

AQS将实现Java锁的一套公共逻辑进行了抽象,定义了一个模版,如果我们想实现自己的锁,则可以继承它来快速实现。JAVA中常见的ReentrantLock,Semaphore(信号量),coutDownLatch,线程池中的Worker等都继承它实现了自己的锁。不过在每个实现类中,state的意义可能不大相同,如可重入锁中代表的是重入的次数/是否有锁,而信号量中代表剩余可以进入的线程数,这些都可以通过看源码来继续学习。

AQS理解之五—并发编程中AQS的理解相关推荐

  1. aqs clh java_Java并发编程:AQS对CLH锁的优化

    自旋锁适用于锁占用时间短,即锁保护临界区很小的情景AQS的自旋锁详解>.它需要保证各缓存数据的一致性,这可能会导致性能问题.因为在多处理器机器上每个线程对应的处理器都对同一个变量进行读写,而每次 ...

  2. JUC里面的相关分类|| java并发编程中,关于锁的实现方式有两种synchronized ,Lock || Lock——ReentrantLock||AQS(抽象队列同步器)

    JUC分类 java并发编程中,关于锁的实现方式有两种synchronized ,Lock AQS--AbstractQueuedSynchronizer

  3. Java 并发编程解析 | 如何正确理解Java领域中的锁机制,我们一般需要掌握哪些理论知识?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 提起Java领域中的锁,是否有种"道不尽红尘奢恋,诉不完人间恩怨"的 ...

  4. java 如何知道对象是否被修改过_Java 并发编程:AQS 的原子性如何保证

    当我们研究AQS框架时(对于AQS不太熟知可以先阅读<什么是JDK内置并发框架AQS>,会发现AbstractQueuedSynchronizer这个类很多地方都使用了CAS操作.在并发实 ...

  5. Java 并发编程解析 | 如何正确理解Java领域中的多线程模型,主要用来解决什么问题?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 我国宋代禅宗大师青原行思在<三重境界>中有这样一句话:" 参禅之初 ...

  6. Java并发编程中的若干核心技术,向高手进阶

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  7. 3.深入理解Java并发编程

    多线程相关知识 多线程的基本概念 什么是cpu CPU的中文名称是中央处理器,是进行逻辑运算用的,主要由运算器.控制器.寄存器三部分组成,从字面意思看就是运算就是起着运算的作用,控制器就是负责发出cp ...

  8. cas无法使用_并发编程中cas的这三大问题你知道吗?

    在java中cas真的无处不在,它的全名是compare and swap,即比较和交换.它不只是一种技术更是一种思想,让我们在并发编程中保证数据原子性,除了用锁之外还多了一种选择. 一.cas的思想 ...

  9. Java 并发编程中的死锁 ( Kotlin 语言讲解)

    什么是死锁? 在操作系统中的并发处理场景中, 进程对资源的持有与请求过程中,会产生死锁. Say, Process A has resource R1 , Process B has resource ...

最新文章

  1. 数据结构源码笔记(C语言):链接栈
  2. 怎么防爬虫爬取信息_scrapy爬取51job职位信息(针对新的反爬虫机制)!
  3. Linux命令及文件操作
  4. [error]Cannot create __weak reference in file using manual refer XCode7.3
  5. linux 7 没有权限访问,[CentOS 7系列]文件或目录的权限与属性
  6. 变量 常量 作用域和命名规范
  7. 结构体交换遇到指针问题和一些记录
  8. 如何快速打通 Docker 镜像发布流程?
  9. python简单爬虫代码-python爬虫超简单攻略,带你写入门级的爬虫,抓取上万条信息...
  10. 怎么建立socket长连接???
  11. 使用Linux快速使用redis集群
  12. 游戏编程--wpe封包教程 (新手必备)
  13. 怎么查看笔记本内存条型号_查看笔记本内存条型号
  14. swift plm物料管理模块中的变更管理介绍
  15. chrome浏览器书签同步_如何将Google Chrome浏览器的书签与手机同步
  16. word论文页码从任意页开始编号
  17. Android 手机获取Mac地址的方法
  18. 拼多多商品发布规则|一度智信
  19. 如何“看懂”图片?谈出海企业的视觉识别体系搭建
  20. element ui框架(准备)

热门文章

  1. php+中午截取,PHP_php字符串截取中文截取2,单字节截取模式,//中文截取2,单字节截取模式 - phpStudy...
  2. oracle语句within,oracle中within group的用法
  3. 使用 Pandas GUI 进行数据探索
  4. 计算机视觉,基于skimage对图像阈值分割的学习
  5. 一、Java入门和环境安装
  6. Jetpack Compose学习笔记
  7. 知识图谱还有哪些方向值得深入研究?这 6 篇最新论文给你答案
  8. 北京大学 微软:预训练模型(Transformer)中的知识神经元
  9. 招聘一周 | 腾讯、华为、微软、中科院自动化所等12家公司热招职位汇总
  10. 论文盘点:性别年龄分类器详解