(笔者推荐)【Java权威指南】「官方文档-中英互译」AQS的源码注释分析,贯穿总体核心流程!

本文主要是介绍(笔者推荐)【Java权威指南】「官方文档-中英互译」AQS的源码注释分析,贯穿总体核心流程!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前提说明

本文主要针对于Java官方文档中的先关的官方注释进行中英文互译,保证了源码坐着的设计思路以及相关知识技能介绍分析等,本文主要进行介绍AQS的源码官方注释的含义介绍,对Java技术有一定领悟的达人会有帮助!


Java官方文档-介绍相关什么是CLH队列(AQS的实现基础)
CLH介绍定义

CLH是基于单向链表的高性能,公平的自旋锁,申请枷锁和线程只需要在本地变量上自旋,来减少CPU缓存和主内存的同步次数,从而降低了总线的内存的开销。


CLH的概念(英文版)

The wait queue is a variant of a “CLH” (Craig,Landin and Hagersten) lock queue. CLH
locks are normally used for spinlocks. We instead use them for blocking synchronizers by including explicit(“prev” and “next”) links plus a “status” field that allow nodes to signal successors when releasing locks, and handle cancellation due to interrupts and timeouts. The status field includes bits that track whether a thread needs a signal (using LockSupport.unpark),Despite these additions,we maintain most CLH locality properties.

CLH的概念(中文版)

这个等待队列是一个CLH锁队列的变体,CLH通常情况下被用来做自旋锁,“我们”改为使用它们来作为实现阻塞同步器,包含线性的(前后)链表,加上一个可以允许在释放锁时候狗后直接点发送一个信号的状态字段,处理相关因为中断超时引起的取消操作,这个状态字段包含位(代码实现时使recheck启用信号和重新检查)可以跟踪一个线程是否需要一个信号,除了这些附加的内容,我们还维护了大多数CLH的本地属性。


CLH的入队操作(英文版)

To enqueue into a CLH lock,you atomically splice it in as new tail. To dequeue, you set the head field,so the next eligible waiter becomes first

CLH的入队操作(中文版)

入队到CLH锁队列时候,自动会将它作为新的队尾节点拼接到队列里面,出队列的时候回设置队首的位置(就是将第一个节点的next指针指向的的节点元素作为队首),也就是下一个等待者成为了队首元素。


CLH的入队操作分析(英文版)

Insertion into a CLH queue requires only a single atomic operation on"tail", so there is a simple point of demarcation from un queued to queued.The"next"link of the predecessor is set by the enqueuing thread after successful CAS.Eventhough non-atomic, this suffices to ensure that any blocked thread is signalled by a predecessor when eligible(although in the case of cancellation, possibly with the assistance of a signal in method clean Queue) .Signalling is based in part on a Dekker-like scheme in which the to-be waiting thread indicates WAITING status, then retries acquiring, and then re checks status before blocking.The signaller atomically clears WAITING status when un parking.

CLH的入队操作分析(中文版)

插入CLH队列需要对队尾节点进行原子操作, 所以有一个简单的入队和出队界定指针,当一个线程CAS成功之后会将自己入队, 设置前一个节点的下一个链接(next) 设置成自己(就是如果一个线程入队成功了, 将前一个节点的next设置成自己)。即使是非原子的这个可以确保任何阻塞的线程在符合条件时(即使在取消的情况下,也可以借助clean Queue方法中的一个信号) 会收到来自前一个节点(node) 的信号, 信号发送部分基于类似De kke的方案, 将要等待的线程表示等待状态,然后重新获取,接下来就是在加锁前重新检测状态。当取消等待(un parking) 的时候,信号发送者会自动清理等待状态。


CLH的出队操作分析(英文版)

Dequeuing on acquire involves detaching(nulling) anode’s “prev"node and then updating the"head”.Other threads check if anode is or was dequeued by checking"prev"rather than head.We enforce the nulling then setting order by spin-waiting if necessary.Because of this, the lock algorithm is not itself strictly"lock-free"because an acquiring thread may need to wait for a previous acquire to make progress.When used with exclusive locks, such progress is required anyway.However Shared mode may(uncommonly) require asp in-wait before setting head field to ensure proper propagation.(Historical note:This allows some simplifications and efficiencies
compared to previous versions of this class.)

CLH的出队操作分析(中文版)

当获取时出队列涉及到了分离(置空)一个节点“prev"节点, 然后更新"head"节点。(其实就是出队列的时候当前节点的prev置空或者分离出去) 。其他的线程需要通过检测"prev"而不是“head"来检测一个节点是否(将要/已经)出队列了(这个地方需要通过检测节点的prev判断是否已经出队列了, 判断head不行, 因为head可能已经出队列了)。如果有必要,我们会强制置空,然后通过自旋等待来设置顺序。因为这个原因,锁算法本身不是严格无锁的,因为一个获取锁的线程可能需要等到上一个获取锁的线程运行。当与排他锁一起使用的时候,像这样的运行无论如何都是必须的。然而在共享模式下可能很罕见)需要一个自旋等待,等待确保前一个进程设置了head字段。


CLH的取消节点任务(英文版)

Anode’s predecessor can change due to cancellation while it is waiting, until the node is first in queue, at which point it
can not change.The acquire method scope with this by rechecking "prev"before waiting.The prev and next fields are modified only via CAS by cancelled nodes in method clean Queue.The un splice strategy is reminiscent of Michael-Scott queues in that after a successful CASto prev field, other threads help fix next fields.Because cancellation often occurs in bunches that complicate decisions about necessary signals, each call to clean Queue traverses the queue until a cleansweep.Nodes that become relinked as first are unconditionally un parked
(sometimes unnecessarily, but those cases are not worth avoiding) .

CLH的取消节点任务(中文版)

一个节点的前一个等待节点,在等待的同时可以被取消操作改变,除非当前节点是队列里面的第一个节点,第一个节点不用变。获取方法在等待之前通过再次检测prev来应对处理(是否要改变上一个节点)。在清空方法队列中的被取消的节点,只能通过CAS操作改变上一个和下一个属性。这个不拼接的策略让人想起Michael-Scott队列, 在这个队列里当对prev字段成功CAS之后,其他的线程帮助修改下一个字段。因为取消操作经常是一大堆一大堆的发生,从而关于必要的信号决策变得很难,每次调用都会对队列遍历,直到清除干净。成为重新链接的节点首先会被无条件的出队列(有时虽然不是必须的,但是这些情况都不值得避免。)


CLH的节点抢占锁(英文版)

A thread may try to acquire if it is first(front most) in the *queue, and sometimes before.Being first does not guarantee *success; it only gives the right to contend.We balance *throughput, overhead, and fairness by allowing incoming threads *to"barge"and acquire the synchronizer while in the process of *enqueuing, in which case an awakened first thread may need to *re wait.To counteract possible repeated unlucky re waits, we *exponentially increase retries(up to 256) to acquire each time *a thread i sun parked.Except in this case, AQS locks do not *spin; they instead interleave attempts to acquire with *bookkeeping steps.(Users who want spinlocks can use *try Acquire.)

CLH的节点抢占锁(中文版)

一个线程如果是队列里面的第一个线程可能会尝试获取锁,第一个线程不能确保成功,在它之前的线程也会去尝试获取锁.它仅仅赋予了竞争的权利。在入队列的过程中,通过允许进来的线程来抢占和获取同步器来平衡吞吐量、开销和公平与否,在这种情况下,一个已经被唤醒的第一个线程可能还会需要重新等待。为了抵消可能出现的反复的不幸的重复等待,我们会在每一次线程出队时,指数的增加重试次数来获取锁。除了这种情况, AQS不自旋。他们反而会交错尝试通过记账步骤来获取锁。(想要自旋锁的用户可以使用try Acquire) .


CLH的GC回收和资源释放(英文版)

To improve garbage collectibility, fields of nodes not yet on list are null.(It is not rare to create and then throwaway a node without using it.) Fields of nodes coming off the list are nulled out as soon as possible.This accentuates the challenge of externally determining the first waiting thread(as in method get First Queued Thread) .This sometimes requires the fallback of traversing backwards from the atomically updated "tail"when fields appear null.(This is never needed in the process of signalling though.)

CLH的GC回收和资源释放(中文版)

为了提高垃圾回收的能力,节点的属性字段还没列出的都先为null(节点创建之后还没有使用就被丢掉是很常见的) 节点的属性字段从list上脱落(不适用的时候) 要尽快置为null.这加剧了从外部确定第一个等待线程的挑战。有时当字段为空的时候,需要从原子更新尾部向后遍历来获得反馈(在处理信号的过程中就从来不需要这个操作).


CLH的GC资源创建及共享和互斥模式(英文版)

CLH queues need a dummy header node to get started.But wed on’t create the mon construction, because it would be wasted effort if there is never contention.Instead, the node is constructed and head and tail pointers are setup on first contention. Shared mode operations differ from Exclusive in that an acquire signals the next waiter to try to acquire if it is also Shared.The try Acquire Shared APl allows users to indicate the degree of propagation, but in most applications, it is more efficient to ignore this, allowing the successor to try acquiring in any case.

CLH的GC资源创建及共享和互斥模式(中文版)

CLH队列不需要从一个假的头节点来开始, 但是我们不在结构上创建,因为如果永远都没有竞争,那么就是白费力气。相反,节点的构造和头尾指针会在第一次竞争时设置好。共享模式操作和排他的操作的区别在于,如果一个获取锁还是共享的话,它会给下一个等待的线程发送信号让它来尝试获取锁。尝试获取共享锁API可以允许用户表明传播程度, 但是在大多数应用程序中允许后者在任何情况下获取锁,会使效率更高。


CLH的资源抢占和授权控制(英文版)

Threads waiting on Conditions use nodes with an additional link to maintain the(FIFO) list of conditions.Conditions only *need to link nodes in simple(non-concurrent) linked queues *because they are only accessed when exclusively held.Upon *await, anode is inserted into a condition queue.Upon signal,*the node is enqueued on the main queue.A special status field *value is used to track and atomically trigger this. Accesses to fields head, tail, and state usefull Volatile mode, along with CAS.Node fields status, prev and next also do *so while threads maybe sign all able, but sometimes use weaker modes otherwise.Accesses to field"waiter"(the thread to be
signalled) are always sandwiched between other atomic accesses so are used in Plain mode.We use jdk.internal Unsafe versions *of atomic access methods rather than Var Handles to avoid
*potential VM bootstrap issues.

CLH的资源抢占和授权控制(中文版)

等待条件的线程使用节点时,需要一个也额外的链表来维护条件链表。条件仅需要使用一个简单的链表队列来链接节点,因为它仅仅实在获取到了排他锁之后才允许访问。(不需要线程安全的,因为是获取到了锁之后才能操作,本身安全) 当调用await方法时, 一个节点被插入到条件队列里,当调用信号方法时,节点会从主要队列中出队列。一个特别的状态字段值被用来跟踪和原子的触发这个条件。(什么条件呢?un park 和park中的那个permits),访问属性字段的头、尾以及状态需要使用完全的Volatile模式一同CAS操作, 当线程可以发信号,节点的状态字段、前后指针也需要Volatile和CAS, 但是有时我们使用较弱的模式。尤其是其他原子访问也是使用普通模式的时候,访问等待线程的字段通常是模棱两可的。我们使用Unsafe 原子访问方法的版本而不使用Var Handles, 为了避免VM启动时出现的潜在的风险。


总结归纳(英文)

Most of the above is performed by primary internal method acquire, that is invoked in some way by all exported acquire methods.(It is usually easy for compilers to optimize
call-site specializations when heavily used.)
There are several arbitrary decisions about when and howto check interrupts in both acquire and await before and/or after blocking.The decisions are less arbitrary in implementation updates because some users appear to relyon original behaviors in ways that are racy and so(rarely) wrong in general but hard to justify changing.
Thanks goto Dave Dice, Mark Moir, Victor Luc hang co, Bill Scherer and Michael Scott, along with members of JSR-166expert group, for helpful ideas, discussions, and critiques on the design of this class.

总结归纳(中文)

上面这些大部分都是通过主要的内部acquire方法来运行,在某种轻度上,所有的暴露出来的获取锁方法都会被调用((过度(频)使用通常让编译器对调用者配置优化变得简单)。上面这些大部分都是通过主要的内部acquire方法来运行,在某种轻度上,所有的暴露出来的获取锁方法都会被调用((过度(频)使用通常让编译器对调用者配置优化变得简单)。

这篇关于(笔者推荐)【Java权威指南】「官方文档-中英互译」AQS的源码注释分析,贯穿总体核心流程!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/594780

相关文章

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分