java开发juc并发之AQS入门
【java开发juc并发之AQS入门】AQS 全称:AbstractQueuedSynchronizer,它是java培训学习中JUC并发工具包中 ReentrantLock 、CountDownLatch、CyclicBarrier等这些类的底层实现。此篇我们主要通过ReentrantLock的使用来大体了解AQS底层代码设计原理,不对源码详细赘述。只要对整体的设计方向有清晰了解,再去追踪源码便不是什么难事。阅读本篇之前,需要大家对ReentrantLock 的API有一定了解。
ReentrantLock的lock、await方法底层逻辑
在使用ReentrantLock进行代码块同步,一般都会有如下写法:
1ReentrantLock lock = new ReentrantLock(true);
2Condition condition = lock.newCondition();
3
4try{
5 lock.lock();
6 //.....
7 condition.await();
8
9 }finally{
10 lock.unlock();
11 }
假设 T1 时刻有10个线程调用同一个ReentrantLock实例的lock()方法, 线程1 先获取锁成功,紧接着线程2 调用lock()方法,发现获取锁失败(通过CAS操作对状态位进行标记),则线程2被封装成Node节点放入AQS双向队列中,并调用LockSupport.park()阻塞挂起。同样的线程3,线程4,…线程10 也阻塞挂起加入队列中。至此在t1时刻 AQS的队列如图1-1:
文章图片
图1-1
T4 时刻线程1执行完毕,调用unlock()方法释放锁并从AQS队列中取出哨兵节点的下一节点,也就是此刻的线程2,并唤醒该线程。线程2再次尝试获取锁,如果锁获取成功将继续执行线程2代码。如果失败会被再次封装成Node节点放入AQS队列中去。看到这里大家可能会有疑问:为什么线程1的锁释放完后唤醒的是线程2,而不是线程3 或者线程 4 呢?这里是因为公平锁与非公平锁的原因,如果采用了公平锁那么将按照先进先出的原则让线程去抢占锁,而非公平锁没有先后顺序的限制。对于ReentrantLock可以通过构造函数的参数进行指定,默认它采用的是非公平锁。
Lock 与 Condition结合使用又是什么样的原理呢?
我们紧接着上面的分析,线程T1时刻获取锁成功后,在T2时刻调用了condition.await()方法时会发生以下4件事情:
- 释放锁(通过CAS操作对状态为进行标记)
- 阻塞挂起线程1
- 线程1封装成Node节点放入条件队列中(注意:此处条件队列和AQS队列不是一回事)
- 唤醒AQS队列中阻塞挂起的线程
一个锁对应一个AQS队列、多个condition、多个条件队列,条件队列的个数是跟随Condition走的。我们通过1-2图来更直观认识 Lock、Condition、AQS队列、条件队列之间的关系。
文章图片
作者:小江
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 深入理解Go之generate
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)