一. 序文ag百家乐网址
1.1 配景先容
跟着多核处理器的普及和散播式计较的泛泛诈骗,并发编程的要害性日益进步。尤其是在散播式系统中,多个线程和节点需要频繁地对分享资源进行并发侦探,从而进步性能和混沌量。掂量词,这也带来了线程安全、资源竞争等问题。为了应酬这些问题,Java 提供了多种并发器用,其中 ReentrantReadWriteLock是一种专为高并发环境假想的锁机制,有用进步了读操作密集型场景下的系统性能。
1.2 YARN 中的并发挑战
YARN(Yet Another Resource Negotiator) 是 Hadoop 生态系统的中枢组件之一,用于资源治理和任务转机。看成一个散播式资源转机框架,YARN 中的资源分派、任务现象更新等操作盛大波及高并发侦探。在这些场景中,既要保证数据一致性,又要幸免性能瓶颈。
YARN 中的一些典型并发需求包括:
资源现象的查询与更新:ResourceManager 需要防备扫数这个词集群的资源现象,包括节点的可用资源、任务的分派情况等。这些现象既需要频繁更新(写操作),又需要复旧高频的查询(读操作)。
任务转机的并发适度:在职务转机经过中,转机器需要同期处理多个节点心跳、诈骗门径资源肯求等,要求系统高效地联接多线程侦探。
集群拓扑的元数据治理:YARN 集群中的节点现象、黑名单更新等操作也需要在多线程环境中高效完成。
ReentrantReadWriteLock 在这些场景中起到了要害作用,通过读写分离进步并发性能的同期,确保了系统的一致性和厚实性。
二. ReentrantReadWriteLock 的基本办法
2.1 什么是 ReentrantReadWriteLock?
ReentrantReadWriteLock 是 Java 并发库中提供的一种高等锁已毕,位于 java.util.concurrent.locks 包中。它是一种复旧读写分离的锁机制,假想策画是在“读多写少”的高并发场景下进步系统性能。与传统的互斥锁(如 ReentrantLock 或 synchronized)不同,ReentrantReadWriteLock 在复旧锁的可重入下,允很多个线程同期读取分享资源(通过读锁),而写操作仍需独占侦探(通过写锁)。
2.2 为什么需要 ReentrantReadWriteLock?
在多线程并发环境中,传统的互斥锁固然能惩处线程安全问题,但由于它只可允许一个线程侦探分享资源,在读操作频繁的场景下,会带来性能瓶颈。ReentrantReadWriteLock 的中枢价值在于分离读锁和写锁,已毕“读并发、写独占”,从而优化性能:
1. 高效的读操作:允很多个线程同期读取资源,而不相互阻碍。
2. 安全的写操作:确保写操作的独占性,保证数据的一致性。
3. 活泼的锁适度:比拟 synchronized,ReentrantReadWriteLock 提供了更多的可控功能(如公说念锁、非公说念锁选拔,复旧锁左迁等)。
2.3 适用场景
ReentrantReadWriteLock 适用于以下场景:
1. 读多写少的高并发场景:
• 缓存系统:读取缓存的频率高于更新缓存的频率。
• 树立治理:多个线程同期读取树立信息,而修改树立的操作较少。
• 日记系统:多个线程同期读取日记数据,写日记的频率较低。
2. 需要活泼适度并刊行动的场景:
• 当系统需要锁左迁、尝试锁取得等高等功能时,ReentrantReadWriteLock 是一个理思选拔。
3. 复杂的现象治理场景:
• 在散播式系统中,举例任务转机或资源治理系统,需要高频查询和一丝更新操作的羼杂场景。
三. ReentrantReadWriteLock 的责任旨趣
3.1 里面结构与假想
ReentrantReadWriteLock 是基于 Java 并发框架的中枢组件 AbstractQueuedSynchronizer (AQS) 已毕的,其里面假想分为以下几个部分:
1. 读锁(ReadLock):已毕为分享锁,允很多个线程同期取得。
2. 写锁(WriteLock):已毕为独占锁,吞并本领惟有一个线程不错抓有写锁。
3. AQS 现象默示:通过一个 32 位整数来防备锁的现象,高 16 位默示读锁现象(即抓有读锁的线程数)。低 16 位默示写锁现象(即抓有写锁的线程的重入次数)。
3.1.1 独享锁与分享锁
ReentrantReadWriteLock的多个线程可同期抓有读锁(分享锁),适用于并发读的场景。但写锁(排他锁)是独占的,ag百家乐可以安全出款的网站当一个线程抓有写锁时,其他线程无法取得写锁或读锁。其中,写锁与写锁互斥(单线程写);写锁与读锁互斥(写操作抵制读操作);读锁与读锁不互斥(多线程并发读)。
独享锁(Exclusive Lock):也称为排它锁。每次只可由一个线程抓有该锁,其他线程在尝试取得锁时会被阻碍。举例 Synchronized 、ReentrantLock 以及ReentrantReadWriteLock writeLock().lock()均是独享锁
分享锁(Shared Lock):允很多个线程同期抓有该锁,但当某个线程肯求独占锁时,扫数分享锁齐必须开释。举例 ReentrantReadWriteLock readLock().lock()
3.1.1 AQS基础
AbstractQueuedSynchronizer(AQS)是 Java 并发包顶用于已毕锁、信号量、条目变量等同步器的基础框架。AQS 通过提供一个基础的同步原语(主要基于现象变量和恭候部队)来简化复杂同步器的已毕。它的假想禁受了 模板法子花样,通过界说一些中枢法子,允许子类字据需要已毕具体的同步逻辑。AQS 的架构特别留神 现象适度 和 线程转机,通过 CAS 操作 和 FIFO 部队 来保证高效的线程治理。
AQS 的假想分为几个主要的模块,分辩是:
1. 同步现象(state)
同步现象 state 是 AQS 的中枢变量,用于记号同步器确面前现象。盛大是一个 int 类型的字段。通过该现象,AQS 适度线程对资源的侦探。
• 读写锁:state 默示面前锁的现象,如读锁数目和写锁是否被占用。
• 信号量:state 默示剩余许可的数目。
• 独占锁:state 为 0 默示锁适意,非 0 默示锁已被占用。
通过 getState()、setState() 和 compareAndSetState() 法子对现象进行操作。state 的值盛大是用来判定是否不错不时奉行某个同步操作。
2. 恭候部队(FIFO 部队)
AQS 里面防备一个 FIFO 双向链表,该部队用来治理扫数恭候资源的线程。每当线程无法取得到同步资源时,它会被加入到该部队中。
• 线程加入部队:当线程取得锁失败时,它会创建一个 Node 对象,加入到部队尾部。
• 线程恭候:线程会在部队中恭候叫醒。当锁开释后,部队头部的线程将被叫醒。
部队中的每个节点默示一个线程。通过该部队,AQS 不错治理多个线程的竞争,并已毕线程的公说念性、优先级等计策。每个 Node 结构包含以下信息:
• thread:恭候线程。• prev:先行者节点。• next:后继节点。• waitStatus:面前节点的现象,决定了节点是否需要被叫醒概况取消。
3. 独占花样与分享花样
AQS 复旧独占花样 和 分享花样两种主要的锁花样。
独占花样:一个线程取得资源后,其他线程无法取得资源,惟有面前抓有锁的线程不错操作该资源。ReentrantLock 即是典型的独占花样已毕。独占花样处理法子如下:
• tryAcquire(int arg):尝试独占锁,失败时参加恭候部队。• tryRelease(int arg):开释独占锁,告捷时叫醒部队中的下一个线程。• acquire(int):取得独占锁,失败时参加部队。• release(int):开释独占锁。
分享花样:多个线程不错同期侦探资源,只消资源糟践。Semaphore 和 CountDownLatch 即是分享花样的已毕。分享花样处理法子如下
• tryAcquireShared(int arg):尝试取得分享锁。• tryReleaseShared(int arg):开释分享锁,叫醒分享花样下的线程。• acquireShared(int):取得分享锁。• releaseShared(int):开释分享锁。
4. AQS 中枢法子与模板法子
AQS 禁受 模板法子假想花样,为子类提供了一些基本的同步框架,并留给子类已毕具体的同步逻辑。子类通过重写以下几个法子来已毕具体的同步器逻辑:
独占花样
• tryAcquire(int):尝试取得独占锁。• tryRelease(int):尝试开释独占锁。• isHeldExclusively():查验是否是面前列程抓有独占锁。
分享花样
• tryAcquireShared(int):尝试取得分享锁。• tryReleaseShared(int):尝试开释分享锁。
恭候部队治理
• addWaiter(Node.EXCLUSIVE):向部队中添加独占线程。• addWaiter(Node.SHARED):向部队中添加分享线程。
AQS 在这些法子中适度线程的同步、列队、叫醒等操作。
5. 节点(Node)
AQS 中的节点 (Node) 是恭候部队中的基本单位。用于默示线程的现象治理线程的列队和叫醒,以及追踪面前列程在恭候部队中的位置。
节点现象:节点的 waitStatus 字段决定了节点的现象
• SIGNAL:面前节点需要被叫醒。• CANCELLED:节点也曾被取消。• CONDITION:节点属于条目部队。• PROPAGATE:分享花样中的传播信号。
节点类型
• EXCLUSIVE:独占花样。• SHARED:分享花样。
6. 现象更新与线程治理
AQS 通过 CAS 操作、compareAndSetState 和 setState 等法子来更新 state,并通过恭候部队治理线程的列队与叫醒。AQS 的基本操作如下:
1. 线程竞争
• 线程率先尝试取得资源,要是告捷,则不时奉行。
• 要是资源被其他线程占用,则参加恭候部队,恭候叫醒。
2. 叫醒与列队
• 当线程开释资源后,AQS 会从恭候部队中叫醒下一个线程(字据恭候部队的 FIFO 轨则)。
3. 现象治理
• AQS 通过 state 适度资源的分派与开释。子类在具体已毕时通过重写 tryAcquire 和 tryRelease 来已毕对资源的适度。
3.2 锁的核情绪制
3.2.1 基于AQS 已毕读写锁
ReentrantReadWriteLock 通过里面界说一个 Sync 类来秉承 AQS 并已毕锁的具体逻辑。它秉承自 AbstractQueuedSynchronizer,认真治理锁的现象。AQS 提供了基本的线程列队和叫醒机制,而 Sync 类通过重写 AQS 的法子来已毕读锁和写锁的取得与开释。在 Sync 类中,读锁和写锁的已毕齐波及到 tryAcquire、tryRelease、tryAcquireShared 和 tryReleaseShared 等法子。ReentrantReadWriteLock 通过秉承 AQS 来已毕其中枢功能。Sync 类的中枢法子已毕:
1. tryAcquireShared():用于尝试取得分享锁(读锁)。 2. tryAcquire():用于尝试取得独占锁(写锁)。 3. tryReleaseShared():开释分享锁。 4. tryRelease():开释独占锁。 5. isWriteLocked():判断面前现象是否为写锁。 6. isReadLocked():判断面前现象是否为读锁。
3.2.2. 读锁的取得与开释取得读锁
高 16 位加 1 默示线程告捷取得读锁,并纪录线程的重入次数
static final int SHARED_SHIFT = 16;static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;static int sharedCount(int c) { return c >>> SHARED_SHIFT; } //右移索求高16位static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } //按位与索求低16位protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); // 取稳妥前列程 int c = getState(); // 取得同步现象 state if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1; // 判断面前是否存在写锁被占用 int r = sharedCount(c); // 取稳妥前的读锁计数(sharedCount(c) 从 state 的高 16 位领略读锁数) if (!readerShouldBlock() && // 判断面前列程是否需要阻碍 r < MAX_COUNT && // 确保读锁计数莫得卓越最大值 compareAndSetState(cag百家乐网址, c + SHARED_UNIT)) { // 使用 CAS 加多读锁计数(state 的高 16 位) if (r == 0) { // 要是面前是第一个取得读锁的线程 firstReader = current; // 将面前列程建造为第一个读线程 firstReaderHoldCount = 1; // 开动化重入计数为 1 } else if (firstReader == current) { firstReaderHoldCount++; // 加多该线程的重入计数 } else { HoldCounter rh = cachedHoldCounter; // 取稳妥前列程的抓锁计数缓存 if (rh == null