java并发工具 AQS为开发者提供了什么? 读写锁设计 C

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

学完AQS,了解到有一些基于AQS的组件,本文在了解读写锁、CountDownLatch、CyclicBarrier、Semaphore的功能后,自己设想一下如何实现这几个功能。

AQS为开发者提供了什么?

一开始的我,看了AQS源码,什么CAS,什么CLH自旋转,什么双向队列,说的贼溜,但是为什么要做呢?我完全不知道。
其实AQS是为了我们开发者提供的,AQS组件让继承者重写以下接口:

  • tryAcquire: 返回true表示当前线程获取锁成功,false表示获取锁失败,进入同步队列
  • tryRelease: 返回true表示释放锁成功
  • tryAcquireShared:与tryAcquire区别是,允许多个线程返回true
  • tryReleaseShared:允许多个线程返回true
  • isHeldExclusively:当前线程是否占有锁

读写锁设计

读写锁功能是:

  1. 读允许并发
  2. 写只能独占

先从简单开始,读写锁,顾名思义,我们先搞两把锁出来:

  • 读锁
  • 写锁

读允许并发,写只能独占。怎么搞?一开始我的想法是两个标志标示正在读,标示正在写。读标志=true,不让其进行write,写标志=true,不让其进行读。但是问题是释放时无法知道何时将标志设置为false。那就只能退而其次,用两个变量进行计数:

  • 读数量
  • 写数量
    当然写的数量最大=1,也可以使用标志进行表示。
    下面就是简单设计一下核心流程了:

怎么允许读线程允许同时进入多个?

AQS tryAcquireShare,读计数通过循环+CAS进行+1操作,返回true表示读锁加成功

当有读线程正在读,此时多个线程加写锁怎么处理?

加写锁的线程进入同步队列,等当前的读计数=0,在唤醒需要加写锁的线程

当有读线程在读,后面有多个写线程加写锁,然后又有线程加读锁,后面的读锁如何处理?

让读线程进入同步队列,等待读线线程前面的写执行完后被唤醒。

从以上场景可以看出,读写锁加锁时需要阻塞的线程是进入同个队列的,所以要用同一个sync的对象进行加锁操作,在抽象出一把读锁,一把写锁提供给使用者。

CountDownLatch

CountDownLatch的效果是,单个线程等待多个线程。
其方法wait可以直接调用sync.accquire(),判断state=0?不为0直接进入阻塞队列。

其方法countDown()可以设计为sync.releaseShare(),当state减少到0时,唤醒阻塞队列。

CyclicBarrier

CyclicBarrier的效果是,每个线程执行wait,然后阻塞等待,知道wait线程的数量达到指定的阈值,执行某个特定的操作。

这个可以通过RetreentLock实现,做一个变量初始化=阈值,每次wait时 通过lock.lock进入,如果变量!=0,则condition.wait(),若达到阈值则执行对应的runnable方法。并唤醒其它线程。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%