多线程进阶之路——AQS 线程同步器

97 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情


开始学习多线程相关知识,大体看了一下,知识点还是挺多的,坚持学习,道阻且长。

AQS(AbstractQueuedSynchronizer)

AQS 提供了一个基于先入先出队列实现的线程同步器的基础框架。ReentrantLock、CountDownLatch、CyclicBarrier、Semaphore 都是基于 AQS 实现的。

AQS 提供两种资源共享方式

  • 互斥访问:任何时候只能存在一个线程访问该共享资源,又可分为公平锁与非公平锁,AQS 默认采用公平策略。
  • 共享访问:需要设置可同时访问共享资源的最大线程数。

核心设计

  • int 类型的 state 变量表示同步状态。使用 volatile 关键字修饰,实现线程的可见性。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。

  • FIFO 线程等待队列(CLH)。 当所有可用资源都被占用时,对于不能访问该共享资源的线程,需要阻塞排队等待,AQS 使用 CLH 队列锁来实现的(CLH 队列是一个虚拟的双向队列,即不存在队列实例,仅存在结点之间的关联关系)。

  • 线程状态的控制。使用 LockSupport 工具类的用于阻塞和唤醒线程的相关方法来对线程的状态进行控制。

  • 自旋和 CAS 机制实现无锁化的线程安全操作。在队列中增删线程节点,或者更新线程节点的状态 waitStatus 时,CAS 和自旋来实现更新和写入操作。

ReentrantLock 可重入锁

支持可重入性,表示能够对共享资源重复加锁,即当前线程再次获取该锁时不会阻塞。

state 等于 0,表示当前没有任何线程占用这个锁,state 大于 0,表示当前存在线程占用这个锁,此时其他线程不能获得锁,而当前成果加锁的线程是可用多次访问使用该 ReentrantLock 对象加锁的其他方法或代码块,每访问一次,state递增 1,实现可重入。

ReentrantLock 功能与 synchronized 关键字类似,但灵活性更好,提供了更多的方法(支持响应中断、超时、尝试获取锁)。

ReentrantLock支持公平锁和非公平锁。

ReentrantReadWriteLock 可重入读写锁

ReentrantReadWriteLock 包含读写两把锁,实现并发读的功能,

  • 对于同一线程而言,读读、写写、写读是共享的,读写是互斥的。
  • 对不同线程而言,读是共享的,读写、写读、写写是互斥的。

CountDownLatch 倒计时同步器

CountDownLatch 主要提供了对多个线程进行协调、控制的作用,可以在主线程等待所有子线程的执行完成。

将状态量 state 定义为倒计时的数值,子线程调用 countDown 方法递减 state,主线程调用 await 方法阻塞等待 state 到 0。

CyclicBarrier 循环栅栏同步器

CyclicBarrier 是一个可循环使用的线程同步器。可以理解为一个栅栏,拦住线程,等到所有的线程都到达则打开栅栏让所有线程继续执行。

相对于 CountDownLatch,CyclicBarrier 可以重复执行,即可以调用 reset 方法,将计数器重置为初始值,同时,可以指定一个 Runnable 任务在栅栏打开时执行。

  • CountDownLatch : 一个或者多个线程,等待其他多个线程完成某件事情之后才能执行
  • CyclicBarrier : 多个线程互相等待,直到到达同一个同步点,再继续一起执行。

CyclicBarrier 是基于 ReentrantLock 和 Condition 来实现的

Semaphore 信号量同步器

Semaphore 主要用于控制资源的可用数量,对于线程优先级方面,存在公平和非公平两种实现,默认为非公平实现。

通过设置同步状态量 state 来定义资源的可用量。

  • 对于公平实现,需要先检查是否有其他线程等待,若有,则进入等待队列。
  • 对于非公平实现,线程直接尝试访问一次资源,若失败则放入等待队列。

因为是零基础学习多线程,很多地方会理解的不到位,作为一个新人,如有错误之处,还请指出^^