皮皮网
皮皮网
囤积线指标源码

【大智慧源码选股】【fdisk源码详解】【红警网游源码】aqs解锁源码_aqs源码解析

时间:2024-11-30 11:40:14 分类:休闲 编辑:重庆导航 源码
1.Java并发编程解析 | 基于JDK源码解析Java领域中并发锁之StampedLock锁的解解析设计思想与实现原理 (三)
2.后端面经-JavaAQS详解
3.信号量(Semaphore)从入门到源码精通
4.33张图解析ReentrantReadWriteLock源码
5.AQS简简单单过一遍
6.ReentrantLock 源码解析 | 京东云技术团队

aqs解锁源码_aqs源码解析

Java并发编程解析 | 基于JDK源码解析Java领域中并发锁之StampedLock锁的设计思想与实现原理 (三)

       在并发编程领域,核心问题涉及互斥与同步。锁源互斥允许同一时刻仅一个线程访问共享资源,源码同步则指线程间通信协作。解解析多线程并发执行历来面临两大挑战。锁源为解决这些,源码大智慧源码选股设计原则强调通过消息通信而非内存共享实现进程或线程同步。解解析

       本文探讨的锁源关键术语包括Java语法层面实现的锁与JDK层面锁。Java领域并发问题主要通过管程解决。源码内置锁的解解析粒度较大,不支持特定功能,锁源因此JDK在内部重新设计,源码引入新特性,解解析实现多种锁。锁源基于JDK层面的源码锁大致分为4类。

       在Java领域,AQS同步器作为多线程并发控制的基石,包含同步状态、等待与条件队列、独占与共享模式等核心要素。JDK并发工具以AQS为基础,实现各种同步机制。

       StampedLock(印戳锁)是基于自定义API操作的并发控制工具,改进自读写锁,特别优化读操作效率。印戳锁提供三种锁实现模式,支持分散操作热点与削峰处理。在JDK1.8中,通过队列削峰实现。

       印戳锁基本实现包括共享状态变量、等待队列、读锁与写锁核心处理逻辑。读锁视图与写锁视图操作有特定队列处理,读锁实现包含获取、释放方式,写锁实现包含释放方式。基于Lock接口的实现区分读锁与写锁。

       印戳锁本质上仍为读写锁,基于自定义封装API操作实现,不同于AQS基础同步器。在Java并发编程领域,多种实现与应用围绕线程安全,根据不同业务场景具体实现。

       Java锁实现与运用远不止于此,还包括相位器、交换器及并发容器中的分段锁。在并发编程中,锁作为实现方式之一,提供线程安全,但实际应用中锁仅为单一应用,提供并发编程思想。

       本文总结Java领域并发锁设计与实现,重点介绍JDK层面锁与印戳锁。文章观点及理解可能存在不足,欢迎指正。技术研究之路任重道远,希望每一份努力都充满价值,未来依然充满可能。fdisk源码详解

后端面经-JavaAQS详解

       AQS是什么?

       AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock。简单来说,AQS定义了一套框架,来实现同步类。

       AQS的核心思想是对于共享资源,维护一个双端队列来管理线程,队列中的线程依次获取资源,获取不到的线程进入队列等待,直到资源释放,队列中的线程依次获取资源。AQS的基本框架如图所示:

       资源state变量表示共享资源,通常是int类型。CLH双向队列是一种基于逻辑队列非线程饥饿的自旋公平锁,具体介绍可参考此篇博客。CLH中每个节点都表示一个线程,处于头部的节点获取资源,而其他资源则等待。Node的方法和属性值如图所示:其中,

       一般来说,一个同步器是资源独占模式或者资源共享模式的其中之一,因此tryAcquire(int)和tryAcquireShared(int)只需要实现一个即可,tryRelease(int)和tryReleaseShared(int)同理。但是同步器也可以实现两种模式的资源获取和释放,从而实现独占和共享两种模式。

       acquire(int)是获取资源的顶层入口,tryAcquire(int)是获取资源的方法,需要自定义同步器实现。addWaiter(Node.EXCLUSIVE)是将线程加入等待队列的尾部,acquireQueued(Node node, int arg)将线程阻塞在等待队列中,直到获取到资源后才返回。

       release(int)是释放资源的顶层入口方法,tryRelease(int)是释放资源的方法,需要自定义同步器自己实现。unparkSuccessor(h)是唤醒后继节点的方法。

       acquireShared(int)和releaseShared(int)是使用共享模式获取共享资源的顶层入口方法,tryAcquireShared(arg)是获取共享资源的方法,doAcquireShared(arg)将线程阻塞在等待队列中,直到获取到资源后才返回。releaseShared(int)是释放共享资源的顶层入口方法,doReleaseShared()方法释放共享资源。

       面试问题模拟:AQS是接口吗?有哪些没有实现的方法?看过相关源码吗?

       A:AQS定义了一个实现同步类的框架,实现方法主要有tryAquire和tryRelease,表示独占模式的资源获取和释放,tryAquireShared和tryReleaseShared表示共享模式的资源获取和释放。源码分析如上文所述。

信号量(Semaphore)从入门到源码精通

       Semaphore是一个用于同步的工具类,在并发编程中扮演重要角色。PV操作是Semaphore的核心操作,P代表获取许可,V代表释放许可。P操作会检查许可是否可用,若可用则获取并返回,否则阻塞直到许可可用;V操作则释放一个许可。

       Semaphore的使用场景主要涉及线程间的同步,比如在资源有限的情况下控制多个线程对资源的访问。例如,红警网游源码当一个队列只允许一定数量的线程同时访问时,可以使用Semaphore来限制队列访问的线程数量。

       使用Semaphore的方式是通过构造方法创建实例,然后使用acquire和release方法来控制许可的获取和释放。acquire方法接受一个参数,表示需要获取的许可数量,默认为1,如果当前可用许可数不足,线程将阻塞直到许可可用。release方法则释放指定数量的许可,通常为1。

       要深入理解Semaphore源码,建议从AQS(AbstractQueuedSynchronizer)的基础开始。AQS提供了对同步器的基本抽象,Semaphore正是基于AQS实现的一种同步工具。

       对于具体源码分析,可以重点关注Semaphore的构造方法、获取锁方法(acquire)以及释放锁方法(release)。在源码中,acquire方法通过调用tryAcquireShared方法尝试获取许可,如果成功则返回true,否则阻塞直到许可可用。release方法则简单地调用releaseShared方法释放一个许可。

       深入学习Semaphore和并发编程,可以参考内核技术中文网的相关资源。该网站提供了一些学习资料和交流社区,包括Linux内核源码学习路线、视频教程、电子书以及实战项目和代码等。此外,网站还定期更新内核技术资料包,供学习者免费获取。

张图解析ReentrantReadWriteLock源码

       今天,我们深入探讨ReentrantReadWriteLock源码,解析其内部结构与工作原理。文章分为多个部分,逐一剖析读写锁的创建、获取与释放过程。

       读写锁规范与实现

       ReentrantReadWriteLock(简称RRW)作为读写锁,其核心功能在于控制并发访问的读与写操作。为了规范读写锁的使用,RRW首先声明了ReadWriteLock接口,并通过ReadLock与WriteLock实现接口,确保读锁与写锁的正确操作。

       为了实现锁的基本功能,WriteLock与ReadLock都继承了Lock接口。这些类内部依赖于AQS(AbstractQueuedSynchronizer)抽象类,AQS为加锁和解锁过程提供了统一的模板函数,简化了锁实现的复杂性。

       核心组件与流程

       AQS提供了一套多线程访问共享资源的同步模板,包括tryAcquire、release等核心抽象函数。WriteLock与ReadLock通过继承Sync类,实现了AQS中的tryAcquire、release(写锁)和tryAcquireShared、tryReleaseShared(读锁)函数。

       Sync类在ReentrantReadWriteLock中扮演关键角色,api 管理 源码它不仅实现了AQS的抽象函数,还通过位运算优化了读写锁状态的存储,减少了资源消耗。此外,Sync类还定义了HoldCounter与ThreadLocalHoldCounter,进一步管理锁的状态与操作。

       公平与非公平策略

       为了适应不同场景的需求,ReentrantReadWriteLock支持公平与非公平策略。通过Sync类的FairSync与NonfairSync子类,实现了读锁与写锁的阻塞控制。公平策略确保了线程按顺序获取锁,而非公平策略允许各线程独立竞争。

       全局图与细节解析

       文章最后,构建了一张全局图,清晰展示了ReentrantReadWriteLock的各个组件及其相互关系。通过深入细节,分别解释了读写锁的创建、获取与释放过程。以Lock接口的lock与unlock方法为主线,追踪了从Sync类出发的实现路径,包括tryAcquire、tryRelease等核心函数,以及它们在流程图中的表现。

       总结,ReentrantReadWriteLock通过继承AQS并扩展公平与非公平策略,实现了高效、灵活的读写锁功能。通过精心设计的Sync类及其相关组件,确保了多线程环境下的并发控制与资源访问优化。深入理解其内部实现,有助于在实际项目中更好地应用读写锁,提升并发性能与系统稳定性。

AQS简简单单过一遍

       回顾前面,AQS的概念被引入,虽然在面试题中有所接触,但对其具体含义并不明晰。本篇内容旨在普及AQS的相关知识,至少能对其概念有一个清晰的认识。

       AQS是java.util.concurrent包下核心类AbstractQueuedSynchronizer的缩写。它作为基础,支撑了诸如ReentrantLock、ReentrantReadWriteLock等锁的实现。

       通读AbstractQueuedSynchronizer的注释,可以总结出它主要包含状态和队列两个关键元素。AQS通过volatile关键字保证状态的线程可见性,使用CAS算法修改状态值。状态值表示锁的持有情况。

       AQS中的队列被称为CLH队列,是一个双向队列,用于管理等待获取锁的线程。队列的组成包括节点结构,每个节点包含状态值、前驱和后继节点的引用。

       在AQS中,acquire和release方法负责获取和释放锁。acquire方法尝试获取锁,若失败,flappybirdc语言源码将线程插入等待队列;在队列中,线程会根据前置节点状态判断是否继续获取,直到获取锁或被中断。release方法唤醒后继节点,若后继节点满足释放条件,则唤醒;否则,从队列尾部向前寻找合适的节点,唤醒符合条件的节点。

       总结而言,AQS是通过状态和队列机制管理锁的获取和释放,支持线程的等待和唤醒,实现线程安全的同步操作。有兴趣的读者可以深入源码和相关资源进行学习,本文旨在提供一个简单的概览。

ReentrantLock 源码解析 | 京东云技术团队

       并发指同一时间内进行了多个线程。并发问题是多个线程对同一资源进行操作时产生的问题。通过加锁可以解决并发问题,ReentrantLock 是锁的一种。

       1 ReentrantLock

       1.1 定义

       ReentrantLock 是 Lock 接口的实现类,可以手动的对某一段进行加锁。ReentrantLock 可重入锁,具有可重入性,并且支持可中断锁。其内部对锁的控制有两种实现,一种为公平锁,另一种为非公平锁.

       1.2 实现原理

       ReentrantLock 的实现原理为 volatile+CAS。想要说明 volatile 和 CAS 首先要说明 JMM。

       1.2.1 JMM

       JMM (java 内存模型 Java Memory Model 简称 JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的是一组规范或者规则,通过这组规范定义了程序中各个变量的访问方式.

       由于 JMM 运行的程序的实体是线程。而每个线程创建时 JMM 都会为其创建一个自己的工作内存 (栈空间), 工作内存是每个线程的私有数据区域。而 java 内存模型中规定所有的变量都存储在主内存中,主内存是共享内存区域,所有线程都可以访问,但线程的变量的操作 (读取赋值等) 必须在自己的工作内存中去进行,首先要将变量从主存拷贝到自己的工作内存中,然后对变量进行操作,操作完成后再将变量操作完后的新值写回主内存,不能直接操作主内存的变量,各个线程的工作内存中存储着主内存的变量拷贝的副本,因不同的线程间无法访问对方的工作内存,线程间的通信必须在主内存来完成。

       如图所示:线程 A 对变量 A 的操作,只能是从主内存中拷贝到线程中,再写回到主内存中。

       1.2.2 volatile

       volatile 是 JAVA 的关键字用于修饰变量,是 java 虚拟机的轻量同步机制,volatile 不能保证原子性。 作用:

       作用:CAS 会使用现代处理器上提供的高效机器级别原子指令,这些原子指令以原子方式对内存执行读 - 改 - 写操作。

       1.2.4 AQSAQS 的全称是 AbstractQueuedSynchronizer(抽象的队列式的同步器),AQS 定义了一套多线程访问共享资源的同步器框架。

       AQS 主要包含两部分内容:共享资源和等待队列。AQS 底层已经对这两部分内容提供了很多方法。

       2 源码解析

       ReentrantLock 在包 java.util.concurrent.locks 下,实现 Lock 接口。

       2.1 lock 方法

       lock 分为公平锁和非公平锁。

       公平锁:

       非公平锁:上来先尝试将 state 从 0 修改为 1,如果成功,代表获取锁资源。如果没有成功,调用 acquire。state 是 AQS 中的一个由 volatile 修饰的 int 类型变量,多个线程会通过 CAS 的方式修改 state,在并发情况下,只会有一个线程成功的修改 state。

       2.2 acquire 方法

       acquire 是一个业务方法,里面并没有实际的业务处理,都是在调用其他方法。

       2.3 tryAcquire 方法

       tryAcquire 分为公平和非公平两种。

       公平:

       非公平:

       2.4 addWaiter 方法

       在获取锁资源失败后,需要将当前线程封装为 Node 对象,并且插入到 AQS 队列的末尾。

       2.5 acquireQueued 方法

       2.6 unlock 方法

       释放锁资源,将 state 减 1, 如果 state 减为 0 了,唤醒在队列中排队的 Node。

       3 使用实例

       3.1 公平锁

       1. 代码:

       2. 执行结果:

       3. 小结:

       公平锁可以保证每个线程获取锁的机会是相等的。

       3.2 非公平锁

       1. 代码:

       2. 执行结果:

       3. 小结:

       非公平锁每个线程获取锁的机会是随机的。

       3.3 忽略重复操作

       1. 代码:

       2. 执行结果:

       3. 小结:

       当线程持有锁时,不会重复执行,可以用来防止定时任务重复执行或者页面事件多次触发时不会重复触发。

       3.4 超时不执行

       1. 代码:

       2. 执行结果:

       3. 小结:

       超时不执行可以防止由于资源处理不当长时间占用资源产生的死锁问题。

       4 总结

       并发是现在软件系统不可避免的问题,ReentrantLock 是可重入的独占锁,比起 synchronized 功能更加丰富,支持公平锁实现,支持中断响应以及限时等待等,是处理并发问题很好的解决方案。

从HotSpot源码,深度解读 park 和 unpark

       我最近建立了一个在线自习室(App:番茄ToDO)用于相互监督学习,感兴趣的小伙伴可以加入。自习室加入码:D5A7A

       Java并发包下的类大多基于AQS(AbstractQueuedSynchronizer)框架实现,而AQS线程安全的实现依赖于两个关键类:Unsafe和LockSupport。

       其中,Unsafe主要提供CAS操作(关于CAS,在文章《读懂AtomicInteger源码(多线程专题)》中讲解过),LockSupport主要提供park/unpark操作。实际上,park/unpark操作的最终调用还是基于Unsafe类,因此Unsafe类才是核心。

       Unsafe类的实现是由native关键字说明的,这意味着这个方法是原生函数,是用C/C++语言实现的,并被编译成了DLL,由Java去调用。

       park函数的作用是将当前调用线程阻塞,而unpark函数则是唤醒指定线程。

       park是等待一个许可,unpark是为某线程提供一个许可。如果线程A调用park,除非另一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上。每次调用一次park,需要有一个unpark来解锁。

       并且,unpark可以先于park调用,但不管unpark先调用多少次,都只提供一个许可,不可叠加。只需要一次park来消费掉unpark带来的许可,再次调用会阻塞。

       在Linux系统下,park和unpark是通过Posix线程库pthread中的mutex(互斥量)和condition(条件变量)来实现的。

       简单来说,mutex和condition保护了一个叫_counter的信号量。当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。当_counter=0时线程阻塞,当_counter>0时直接设为0并返回。

       每个Java线程都有一个Parker实例,Parker类的部分源码如下:

       由源码可知,Parker类继承于PlatformParker,实际上是用Posix的mutex和condition来实现的。Parker类里的_counter字段,就是用来记录park和unpark是否需要阻塞的标识。

       具体的执行逻辑已经用注释标记在代码中,简要来说,就是检查_counter是不是大于0,如果是,则把_counter设置为0,返回。如果等于零,继续执行,阻塞等待。

       unpark直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程。源码如下:

       (如果不会下载JVM源码可以后台回复“jdk”,获得下载压缩包)

AbstractQueuedSynchronizer详解

       AbstractQueuedSynchronizer,简称AQS,究竟是什么呢?

       它提供了一个框架,用于实现基于先进先出(FIFO)等待队列的阻塞锁及相关同步器(如信号量、事件等)。这个类的设计目的是为了成为那些依赖单一原子整数值来表示状态的同步器的有效基础。

       从JDK的注释来看,AQS是一个实现阻塞锁和相关同步器的框架,它基于先进先出的等待队列。这个类适用于大多数使用整数来表示状态的同步器。这里提到的框架实际上是一个类。

       那么,在什么情况下我们会使用AQS呢?

       在编程中,我们经常遇到并发问题。有了并发,就涉及资源共享;而资源共享又需要处理资源的同步访问。在处理同步时,我们需要解决竞争发生时的等待问题和竞争解除后的唤醒问题。AQS就是这样一个便于实现这种同步机制的框架。我们日常使用的ReentrantLock、ReentrantReadWriteLock以及ArrayBlockingQueue等都是基于AQS实现的。

       如果没有AQS,又会如何呢?

       在没有AQS的情况下,要实现锁需要怎么做呢?这些问题都是我们在实现锁时需要考虑的。

       在AQS的情况下,情况又是怎样的呢?

       AQS作为基础类,主要解决了在锁不可用时的等待,以及锁释放后的唤醒。锁状态的设定、如何获取锁以及如何释放锁,都是需要相应的同步机制自己实现的。因此,在使用AQS时,需要实现以下方法:

       类似于ReentrantLock,使用state来标识锁的状态,state = 0表示锁未被获取,当state > 0表示锁已被获取。此外,实现了AQS的tryAcquire()和tryRelease()来处理state的状态,以处理锁的状态。这样就可以实现基础的ReentrantLock。因此,在AQS的支持下,实现类似的阻塞锁非常方便。

       以上我们介绍了AQS是什么,以及AQS的具体使用场景,下面我们详细介绍AQS的具体实现机制。

       我们先简单看一下AQS的核心流程。

       接下来,我们将结合ReentrantLock来详细查看基于AQS如何实现排他锁。

       首先,我们来看一下ReentrantLock是如何使用int类型的state来表示锁的状态的。state = 0表示锁未被获取。state > 0表示锁已被持有。由于是可重入锁,state表示了这个锁被同一个线程获取了多少次。

       上面的代码片段是ReentrantLock的核心代码,为了便于解释,省略了其他代码。首先我们可以看到,在使用AQS时,通常不是直接实现AQS,而是创建一个内部的辅助类。

       Subclasses should be defined as non-public internal helper classes that are used to implement the synchronization properties of their enclosing class

       然后加锁和释放锁对应的lock和unlock,直接调用辅助类的lock和unlock。

       下面我们来看一下Sync类的代码。

       上面我们来看一下非公平锁NonfairSync的实现。

       下面我们来看一下AQS中的acquire方法。

       上面我们看了获取锁的方法acquire,下面看下释放锁的release。

       总结一下,AQS通过tryAcquire以及tryRelease两个方法,来进行锁的获取以及释放,两个都是非阻塞的方法。如果获取成功则返回,如果获取失败,则添加队列。在队列中获取锁,获取失败则挂起,等待锁被释放后被重新唤醒。唤醒后还是会去尝试获取锁。释放锁的时候如果检查到已经全部释放,则会唤醒被挂起的线程。这样通过tryAcquire和tryRelease,实现了锁的获取以及等待,以及锁的释放。具体锁状态的控制,子类则是通过tryAcquire和tryRelease进行控制的。

       在介绍ReentrantLock时,我们简化了很多代码。了解基本原理后,再去读源码会事半功倍。

ReentrantLock源码详细解析

       在深入解析ReentrantLock源码之前,我们先了解ReentrantLock与同步机制的关系。ReentrantLock作为Java中引入的并发工具类,由Doug Lea编写,相较于synchronized关键字,它提供了更为灵活的锁管理策略,支持公平与非公平锁两种模式。AQS(AbstractQueuedSynchronizer)作为实现锁和同步器的核心框架,由AQS类的独占线程、同步状态state、FIFO等待队列和UnSafe对象组成。AQS类的内部结构图显示了其组件的构成。在AQS框架下,等待队列采用双向链表实现,头结点存在但无线程,T1和T2节点中的线程可能在自旋获取锁后进入阻塞状态。

       Node节点作为等待队列的基本单元,分为共享模式和独占模式,值得关注的是waitStatus成员变量,它包含五种状态:-3、-2、-1、0、1。本文重点讨论-1、0、1状态,-3状态将不涉及。非公平锁与公平锁的差异在于,非公平锁模式下新线程可直接尝试获取锁,而公平锁模式下新线程需排队等待。

       ReentrantLock内部采用非公平同步器作为其同步器实现,构造函数中根据需要选择非公平同步器或公平同步器。ReentrantLock默认采用非公平锁策略。非公平锁与公平锁的区别在于获取锁的顺序,非公平锁允许新线程跳过等待队列,而公平锁严格遵循队列顺序。

       在非公平同步器的实例中,我们以T1线程首次获取锁为例。T1成功获取锁后,将exclusiveOwnerThread设置为自身,state设置为1。紧接着,T2线程尝试获取锁,但由于state为1,获取失败。调用acquire方法尝试获得锁,尝试通过tryAcquire方法实现,非公平同步器的实现调用具体逻辑。

       在非公平锁获取逻辑中,通过CAS操作尝试交换状态。交换成功后,设置独占线程。当当前线程为自身时,执行重入操作,叠加state状态。若获取锁失败,则T2和T3线程进入等待队列,调用addWaiter方法。队列初始化通过enq方法实现,enq方法中的循环逻辑确保线程被正确加入队尾。新线程T3调用addWaiter方法入队,队列初始化完成。

       在此过程中,T2和T3线程开始自旋尝试获取锁。若失败,则调用parkAndCheckInterrupt()方法进入阻塞状态。在shouldParkAfterFailedAcquire方法中,当前驱节点等待状态为CANCELLED时,方法会找到第一个非取消状态的节点,并断开取消状态的前驱节点与该节点的连接。若T5线程加入等待队列,T3和T4线程因为自旋获取锁失败进入finally块调用取消方法,找到等待状态不为1的节点(即T2),断开连接。

       理解了shouldParkAfterFailedAcquire方法后,我们关注acquireQueued方法的实现。该方法确保线程在队列中正确释放,如果队列的节点前驱为head节点,成功获取锁后,调用setHead方法释放线程。setHead方法通过CAS操作更新head节点,释放线程。acquire方法中的阻塞是为防止线程在唤醒后重新尝试获取锁而进行的额外阻断。

       锁的释放过程相对简单,将state减至0,将exclusiveOwnerThread设置为null,完成锁的释放。通过上述解析,我们深入理解了ReentrantLock的锁获取、等待、释放等核心机制,为并发编程提供了强大的工具支持。

本文地址:http://0553.net.cn/html/9d615993831.html

热点资讯

copyright © 2016 powered by 皮皮网   sitemap