AQS与Synchronized异曲同工的加锁流程

在并发多线程的情况下,为了保证数据安全性,一般我们会对数据进行加锁,通常使用Synchronized或者ReentrantLock同步锁。Synchronized是基于JVM实现,而ReentrantLock是基于Java代码层面实现的,底层是继承的AQS。

AQS全称AbstractQueuedSynchronizer,即抽象队列同步器,是一种用来构建锁和同步器的框架。它维护了一个共享资源state(volatile修饰) + 一个双向链表结构的同步队列 + 一个单链表结构的条件队列,底层利用了CAS机制来保证操作的原子性

AQS通过控制state变量(volatile的int类型)的值来判断锁的状态,对于不可重入锁state不是0则阻塞;对于可重入锁如果state=0则执行抢占,非0则判断当前线程是否是占有锁的线程,是就把state加1,比如重入5次,那么state加1执行5次,可重入锁在释放锁的时候,同样需要释放5次直到state=0其他线程才有资格获得锁。

我们常见的并发锁ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier都是基于AQS实现的。

仔细研究AQS底层的加锁原理,其实跟Synchronized的加锁原理有惊人的相似。

Synchronized的加锁流程

这里直接讨论重量级锁,忽略锁升级的过程(对锁升级过程感兴趣可移步Java对synchronized锁的实现与优化),重量级锁底层使用操作系统的互斥锁来实现。

考虑Synchronized的加锁流程,Synchronized的对象锁要能满足以下几个场景:

  1. 多个线程执行到Synchronized代码块,只有一个线程获取锁,然后执行同步代码块(需要记录哪个线程获取了对象锁)。
  2. 其他线程被阻塞(被阻塞的线程,是不是可以设计一个阻塞队列)
  3. 持有锁的线程调用wait方法释放锁,等待被唤醒(等待的线程,是不是可以设计一个等待队列)
  4. 被阻塞的线程开始竞争锁
  5. 调用notify方法,唤醒等待的线程,被唤醒的线程进入阻塞队列,一块竞争锁

上面描述了Synchronized的加锁流程,Synchronized的对象锁存储结构如下图所示。

对象锁的基本工作机制:

  1. 当多个线程同时访问一段同步代码时,首先会进入 _EntryList队列中阻塞
  2. 当某个线程获取到对象的对象锁后进入临界区域,并把对象锁中的_owner变量设置为当前线程,即获得对象锁
  3. 若持有对象锁的线程调用wait() 方法,将释放当前持有的对象锁,_owner变量恢复为null,同时该线程进入_WaitSet 集合中等待被唤醒
  4. 在WaitSet集合中的线程被唤醒,会被再次放到EntryList队列中,重新竞争获取锁
  5. 若当前线程执行完毕也将释放对象锁并复位变量的值,以便其他线程进入获取

可以发现,Synchronized对象锁存储结构完美符合最初设想时它需要满足的场景。

AQS的加锁流程

照例,先分析使用AQS的加锁需求:

  1. 多个线程执行到acquire方法的时候,只有一个线程获取锁然后执行同步代码块(需要记录哪个线程获取了对象锁)
  2. 其他线程被阻塞(被阻塞的线程,是不是可以设计一个阻塞队列)
  3. 持有锁的线程调用await方法,释放锁,等待被唤醒(等待的线程,是不是可以用设计个等待队列)
  4. 被阻塞的线程开始竞争锁
  5. 调用signal方法,唤醒等待的线程,被唤醒的线程进入阻塞队列,一块竞争锁

AQS的加锁需求跟Synchronized是一样的。AQS实际的加锁机制是怎么设计的呢?如下图所示。

参考上图,可以发现AQS的加锁流程并不复杂,只要理解了同步队列和条件队列,以及它们之间的数据流转,就算彻底理解了AQS。

  1. 当多个线程竞争AQS锁时,如果有个线程获取到锁,就把ower线程设置为自己
  2. 没有竞争到锁的线程,在同步队列中阻塞(同步队列采用双向链表,CAS尾插)
  3. 持有锁的线程调用await方法,释放锁,追加到条件队列的末尾(条件队列采用单链表,尾插)
  4. 持有锁的线程调用signal方法,唤醒条件队列的头节点,并转移到同步队列的末尾
  5. 同步队列的头节点优先获取到锁

可以看到AQS和Synchronized的加锁流程几乎是一模一样的,AQS中同步队列就是Synchronized中EntryList,AQS中条件队列就是Synchronized中的waitSet,两个队列之间的数据转移流程也是一样的,阻塞状态的线程被放到同步队列中,等待状态的线程被放到条件队列中,从条件队列唤醒的线程又被转移到同步队列末尾,一块竞争锁。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值