当前位置: 首页 > news >正文

外贸球衣网站交换链接案例

外贸球衣网站,交换链接案例,web响应式网页设计,wordpress主题 dux1.2原版ReentrantReadWriteLock 提供了读写分离的锁机制,允许多个线程同时获取读锁,但写锁是独占的。它支持可重入、公平/非公平策略、锁降级等高级特性。 内部类结构 Sync:抽象同步器,基于 AbstractQueuedLongSynchronizerReadLock/Wr…

ReentrantReadWriteLock 提供了读写分离的锁机制,允许多个线程同时获取读锁,但写锁是独占的。它支持可重入、公平/非公平策略、锁降级等高级特性。

内部类结构

  • Sync:抽象同步器,基于 AbstractQueuedLongSynchronizer
  • ReadLock/WriteLock:分别封装读写锁操作
  • FairSync/NonfairSync:公平性策略实现

线程安全保证

  • 使用 CAS 操作保证状态更新的原子性
  • 基于 AQS 的等待队列管理阻塞线程
  • 通过 volatile 变量和内存屏障保证可见性

1. 核心能力概述

读写分离机制

  • 读锁共享:多个线程可以同时持有读锁
  • 写锁独占:只有一个线程可以持有写锁
  • 读写互斥:读锁和写锁不能同时被持有

2. 关键特性详解

2.1 可重入性

// 支持同一线程多次获取同一类型的锁
if (firstReader == current) {firstReaderHoldCount++;
} else {// 通过 HoldCounter 记录每个线程的持有次数rh.count++;
}

2.2 公平性策略

  • 非公平模式(默认)NonfairSync
    final boolean writerShouldBlock() {return false; // 写线程可以直接抢占
    }
    
  • 公平模式FairSync 
    final boolean writerShouldBlock() {return hasQueuedPredecessors(); // 检查是否有等待更久的线程
    }
    

2.3 锁降级支持

支持从写锁降级到读锁,但不支持从读锁升级到写锁:

// 典型的锁降级模式
rwl.writeLock().lock();
try {// 更新数据rwl.readLock().lock(); // 在释放写锁前获取读锁
} finally {rwl.writeLock().unlock(); // 释放写锁,仍持有读锁
}

状态编码设计

使用一个 long 型变量同时记录读写锁状态:

static final int SHARED_SHIFT   = 32;
static final long SHARED_UNIT    = (1L << SHARED_SHIFT);
static final long EXCLUSIVE_MASK = (1L << SHARED_SHIFT) - 1;// 高32位:读锁计数
static int sharedCount(long c)    { return (int)(c >>> SHARED_SHIFT); }
// 低32位:写锁计数  
static int exclusiveCount(long c) { return (int)(c & EXCLUSIVE_MASK); }

写锁获取逻辑

写锁 (tryAcquire) 的逻辑相对简单:

  • 独占性: 写锁是独占的,任何时候最多只有一个线程可以持有写锁。
  • 状态检查:
    • 如果当前没有锁 (state == 0),则尝试通过 CAS 获取锁。如果失败(比如因为公平策略 writerShouldBlock() 返回 true,或者 CAS 竞争失败),tryAcquire 直接返回 false。后续的排队和唤醒逻辑由 AQS 框架处理。
    • 如果当前已有锁 (state != 0):
      • 如果存在读锁 (写计数 w 为 0),则获取失败。
      • 如果存在写锁,但持有者不是当前线程,则获取失败。
      • 如果当前线程已持有写锁(重入),则直接增加写锁计数。
  • 重入处理: 写锁的重入计数直接记录在 state 的低位。判断重入和增加计数相对直接。
  • AQS 框架: 对于获取失败的情况(例如,锁被其他线程持有,或 CAS 失败),AQS 框架会自动处理线程的入队、阻塞和后续的唤醒重试。tryAcquire 只需要负责单次尝试获取的逻辑。

因此,写锁的 tryAcquire 方法已经能够完整地处理单次尝试获取锁的各种情况,包括重入。如果一次 tryAcquire 失败,AQS 会负责后续的流程,不需要一个额外的 "full" 版本来进行内部循环重试或处理更复杂的重入逻辑。

protected final boolean tryAcquire(long acquires) {Thread current = Thread.currentThread();long c = getState();long w = exclusiveCount(c);if (c != 0) {// 1. 如果有读锁或其他线程持有写锁,失败if (w == 0 || current != getExclusiveOwnerThread())return false;// 2. 重入检查if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");setState(c + acquires);return true;}// 3. 尝试获取写锁if (writerShouldBlock() || !compareAndSetState(c, c + acquires))return false;setExclusiveOwnerThread(current);return true;
}

readHolds 和 cachedHoldCounter 

位于 ReentrantReadWriteLock.java 中 Sync 类内部。

这两个字段都与读锁的重入计数有关。ReentrantReadWriteLock 允许同一个线程多次获取读锁(或写锁),这种行为称为重入。

  1. readHolds:

    ReentrantReadWriteLock.java

        /*** The number of reentrant read locks held by current thread.* Initialized only in constructor and readObject.* Removed whenever a thread's read hold count drops to 0.*/private transient ThreadLocalHoldCounter readHolds;
    
    • 类型readHolds 是一个 ThreadLocalHoldCounter 类型的成员变量。ThreadLocalHoldCounter 继承自 ThreadLocal<HoldCounter>。这意味着每个线程都有自己独立的 HoldCounter 副本。
    • HoldCounter: 这个内部类非常简单,它包含两个字段:
      • int count: 表示当前线程持有读锁的重入次数。
      • final long tid: 当前线程的ID,用于避免在 HoldCounter 对象生命周期结束后依然持有对 Thread 对象的引用,从而防止内存泄漏。
      /*** A counter for per-thread read hold counts.* Maintained as a ThreadLocal; cached in cachedHoldCounter.*/
      static final class HoldCounter {int count;          // initially 0// Use id, not reference, to avoid garbage retentionfinal long tid = LockSupport.getThreadId(Thread.currentThread());
      }/*** ThreadLocal subclass. Easiest to explicitly define for sake* of deserialization mechanics.*/
      static final class ThreadLocalHoldCounterextends ThreadLocal<HoldCounter> {public HoldCounter initialValue() {return new HoldCounter();}
      }
      
    • 作用readHolds 的核心作用是精确地追踪和管理每个线程获取读锁的重入次数。当一个线程第一次获取读锁时,会为其创建一个 HoldCounter 并存入其线程本地存储中;之后每次重入获取读锁,对应 HoldCounter 的 count 就会增加;每次释放读锁,count 就会减少。当一个线程的 HoldCounter 的 count 降为0时,表示该线程不再持有读锁,此时会从 ThreadLocal 中移除这个 HoldCounter
  2. cachedHoldCounter:

    ReentrantReadWriteLock.java

        /*** The hold count of the last thread to successfully acquire* readLock. This saves ThreadLocal lookup in the common case* where the next thread to release is the last one to* acquire. This is non-volatile since it is just used* as a heuristic, and would be great for threads to cache.** <p>Can outlive the Thread for which it is caching the read* hold count, but avoids garbage retention by not retaining a* reference to the Thread.** <p>Accessed via a benign data race; relies on the memory* model's final field and out-of-thin-air guarantees.*/private transient HoldCounter cachedHoldCounter;
    
    • 类型cachedHoldCounter 是一个 HoldCounter 类型的成员变量。
    • 作用: 这是一个性能优化的手段。它缓存了最后一个成功获取读锁的线程的 HoldCounter
    • 优化原理: 在很多情况下,一个线程获取读锁后,紧接着就会释放该读锁。如果下一个释放读锁的线程恰好是上一个获取读锁的线程,那么就可以直接使用 cachedHoldCounter 中缓存的 HoldCounter,而无需通过 readHolds.get() 进行 ThreadLocal 的查找。ThreadLocal 的查找相对而言会有一些开销,通过这种缓存机制可以减少这种开销,尤其是在读锁竞争不激烈或者单个线程连续操作读锁的场景下。
    • 非 volatile: 注释中明确指出它是非 volatile 的,因为它仅仅作为一个启发式的缓存。即使存在数据竞争(benign data race),最坏的情况也只是缓存失效,需要回退到 ThreadLocal 查找,不影响正确性。
    • 避免内存泄漏: 和 HoldCounter 内部使用 tid 而不是直接引用 Thread 对象一样,cachedHoldCounter 也只持有 HoldCounter,而 HoldCounter 内部通过 tid 间接关联线程,从而避免了因为缓存导致线程对象无法被垃圾回收的问题。

 读锁获取逻辑

tryAcquireShared 的设计目标是快速路径:

  • 它尝试以最小的开销处理最常见的情况:
  • 非重入或简单重入 (当前线程是 firstReader)。
  • 一次 CAS 成功。
  • 它故意推迟了对复杂重入情况(非 firstReader 的重入,需要访问 ThreadLocal)的处理,以避免在常见路径上产生 ThreadLocal.get() 的开销。
protected final long tryAcquireShared(long unused) {Thread current = Thread.currentThread();long c = getState();// 1. 检查是否有其他线程持有写锁if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)return -1L;// 2. 尝试获取读锁int r = sharedCount(c);if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {// 成功获取,更新持有计数return 1L;}return fullTryAcquireShared(current);
}

当 tryAcquireShared 的快速路径失败时(例如 CAS 失败,或者需要处理复杂的重入逻辑),就会调用 fullTryAcquireShared。这个方法包含了更完整的逻辑:

  • 循环重试 (CAS Misses)for (;;) 循环允许在 CAS 操作失败后进行重试。这是因为多个读线程可能同时尝试增加读锁计数,导致 CAS 竞争。
  • 完整的重入处理: 它能正确处理所有线程的读锁重入,包括查找和更新 ThreadLocal 中的 HoldCounter

    ReentrantReadWriteLock.java

            // ...HoldCounter rh = null; // rh 会在需要时从 readHolds 或 cachedHoldCounter 获取for (;;) {long c = getState();if (exclusiveCount(c) != 0) { // 检查写锁if (getExclusiveOwnerThread() != current)return -1; // 其他线程持有写锁,失败// else we hold the exclusive lock; blocking here// would cause deadlock. (当前线程持有写锁,允许获取读锁 - 锁降级)} else if (readerShouldBlock()) { // 根据策略判断是否应阻塞// Make sure we're not acquiring read lock reentrantly// 如果需要阻塞,但当前是重入读,则不应该阻塞。// 这里的逻辑是确保如果 readerShouldBlock() 为 true,// 并且当前不是重入(即 rh.count == 0),则获取失败。if (firstReader == current) {// assert firstReaderHoldCount > 0;} else {if (rh == null) { // 首次进入或 rh 未初始化rh = cachedHoldCounter;if (rh == null ||rh.tid != LockSupport.getThreadId(current)) {rh = readHolds.get(); // 从 ThreadLocal 获取if (rh.count == 0) // 如果 ThreadLocal 中计数为0,说明之前没有正确移除或状态异常readHolds.remove();}}if (rh.count == 0) // 如果当前线程的读锁计数为0,并且 readerShouldBlock() 为 true,则获取失败return -1L;}}if (sharedCount(c) == MAX_COUNT) // 读锁数量超限throw new Error("Maximum lock count exceeded");if (compareAndSetState(c, c + SHARED_UNIT)) { // 尝试CAS增加读锁计数// ... 成功获取后更新 firstReader/firstReaderHoldCount 或 rh.count ...return 1L; // 成功}}
    
  • 处理锁降级: 在循环中,如果发现写锁被持有 (exclusiveCount(c) != 0),它会检查是否是当前线程持有。如果是,则允许继续尝试获取读锁(这是锁降级的关键)。
  • 更细致的策略检查: 在循环的每次迭代中都会检查 readerShouldBlock()

readerShouldBlock

非公平情况下,会检查第一个是不是读锁。

公平情况下,会检查前面有没有头节点之外的节点,这意味着第二个读线程可以通过这个判断,然后第三个。。。。直到一个写节点加入。

writerShouldBlock

writerShouldBlock() 是 ReentrantReadWriteLock.Sync 类中的一个抽象方法,它在两个具体的子类 FairSync 和 NonfairSync 中有不同的实现。这个方法的核心作用是判断当前尝试获取写锁的线程是否应该因为特定的策略(公平性策略)而被阻塞,即使锁当前可能没有被其他线程持有。

来看一下它在 ReentrantReadWriteLock.Sync 类中的 tryAcquire 方法中是如何被使用的:

        @Override@ReservedStackAccessprotected final boolean tryAcquire(long acquires) {// ... (省略了检查是否已有读锁或写锁被其他线程持有的逻辑) ...Thread current = Thread.currentThread();long c = getState();long w = exclusiveCount(c);if (c != 0) {// ... (处理已有锁的情况,包括重入) ...}// 当 c == 0 时,表示当前没有任何锁(既没有读锁也没有写锁)// 此时,即使锁是可用的,也需要根据策略判断是否应该阻塞if (writerShouldBlock() || // <--- 在这里被调用!compareAndSetState(c, c + acquires))return false; // 如果 writerShouldBlock() 返回 true,则获取失败,线程需要去排队setExclusiveOwnerThread(current);return true;}

当 tryAcquire 方法执行到 c == 0 的分支时,意味着当前锁是自由的(没有线程持有读锁或写锁)。在这种情况下:

  1. 调用 writerShouldBlock():

    • 这个方法的返回值决定了当前线程是否应该“礼让”其他可能在等待队列中的线程。
    • 如果 writerShouldBlock() 返回 true,即使锁是可用的,tryAcquire 也会返回 false。这意味着当前线程获取写锁的尝试失败了,它将被 AQS 框架放入等待队列中。
    • 如果 writerShouldBlock() 返回 false,则当前线程可以继续尝试通过 compareAndSetState (CAS) 来获取写锁。
  2. compareAndSetState(c, c + acquires):

    • 如果 writerShouldBlock() 返回 false,则会尝试通过 CAS 操作来原子地更新锁的状态,从而获取写锁。
    • 如果 CAS 失败(例如,在非公平模式下,另一个线程恰好在此时也尝试获取锁并成功了),tryAcquire 同样返回 false

不同子类中的实现:

  • NonfairSync (非公平同步器):

    ReentrantReadWriteLock.java

    static final class NonfairSync extends Sync {// ...final boolean writerShouldBlock() {return false; // writers can always barge}// ...
    }
    

    在非公平模式下,writerShouldBlock() 总是返回 false。这意味着尝试获取写锁的线程总是可以“插队”或“闯入”(barge),它会直接尝试通过 CAS 获取锁,而不会检查等待队列中是否有其他线程。

  • FairSync (公平同步器):

    static final class FairSync extends Sync {// ...final boolean writerShouldBlock() {return hasQueuedPredecessors();}// ...
    }
    

    在公平模式下,writerShouldBlock() 调用 hasQueuedPredecessors()。这个方法(继承自 AQS)会检查当前线程在同步队列中是否有前驱节点(即是否有其他线程比它更早开始等待获取锁)。

    • 如果 hasQueuedPredecessors() 返回 true,表示队列中有等待时间更长的线程,那么当前线程就应该阻塞,让等待时间长的线程先获取锁,以保证公平性。
    • 如果返回 false,表示当前线程是队列的头部或者队列为空,它可以尝试获取锁。

持有计数优化

使用多级缓存机制优化读锁计数:

// 第一个读线程的快速路径
private transient Thread firstReader;
private transient int firstReaderHoldCount;// 最近访问线程的缓存
private transient HoldCounter cachedHoldCounter;// 线程本地存储
private transient ThreadLocalHoldCounter readHolds;

    使用场景

    适用于读多写少的并发场景,如:

    • 缓存系统的读写操作
    • 配置信息的访问和更新
    • 统计数据的查询和修改

    通过读写分离,ReentrantReadWriteLock 相比普通的互斥锁能显著提升读并发性能。

    http://www.dt0577.cn/news/49757.html

    相关文章:

  • 做淘宝京东还是独立网站正规电商培训班
  • 如何看网站做的好坏互动营销经典案例
  • 北京网站开发网站建设咨询新闻稿代写平台
  • 三端互通传奇手游开服列表站长工具seo综合查询论坛
  • 电子商务网站建设选择自媒体seo优化
  • 做日用品有什么网站好seo教程之关键词是什么
  • 传奇新开网站媒体网络推广价格优惠
  • 优秀平面设计作品网站网站推广优化公司
  • 做网站具体流程福州短视频seo平台
  • 石家庄市建设厅网站google中文搜索引擎
  • 外贸网站建设十大标准外贸网站建站互联网广告公司
  • 郑州北环网站建设培训自制网页
  • 京东第一次做网站360应用商店
  • 手机商城积分兑换安康seo
  • 做旅游网站的目的与意义网站建设方案书范文
  • 聚搜济南网站建设公司百度推广最简单方法
  • 有什么做任务得佣金的网站黑帽seo寄生虫
  • 福州做企业网站的公司找文网客服联系方式
  • 寄生虫网站怎么做惠州企业网站建设
  • 自己做网站教程友情链接交换平台免费
  • ab模板网官网seo搜索优化是什么呢
  • 记事本做网站插图片seo属于技术还是营销
  • 网站设置搜索关键字百度网站免费优化软件下载
  • 框架布局技术制作一个网站百度识图在线使用一下
  • 网站建设中 倒计时seo内容优化方法
  • 模板网站做外贸好不好兰州网络推广的平台
  • 建站方案策划书如何开发一款app软件
  • 政府网站建设现状和存在的问题站长之家工具
  • 潍坊设计网站建设百度站长平台链接提交
  • 大学网站建设专业互联网运营推广是做什么的