黑马满老师讲JUC
同步笔记1. * AQS 原理
😖AQS 原理了解么?AQS 组件有哪些?
满老师讲AQS08.101-aqs-概述_哔哩哔哩_bilibili
面试官:谈谈你对AQS的理解吧 这波回答 你打几分?_哔哩哔哩_bilibili
从AQS的整体概念、核心思想、和CLH队列说
- AQS中文是一个抽象的队列同步器。它是 实现锁或者其他同步器组件 的抽象实现
- AQS 核心思想是
如果被请求的 共享资源 空闲,则将当前请求资源的线程 设置为有效的工作线程,并且将 共享资源 上锁(设置state变量)。
如果被请求的共享资源被 占用,那么就需要一套 线程阻塞等待 以及 被唤醒时 锁分配 的机制, AQS机制基于CLH锁思想。
- AQS结构:AQS/CLH 维护了一个虚拟双向队列,阻塞等待的线程被加到队列中, 每个节点双向连接。
AQS 将每条请求共享资源的线程封装成一个 CLH 队列的一个结点(Node)来实现锁的分配。
(AQS其实只存了head节点和tail节点的引用,和一个int的state变量表示同步状态)
每个Node节点表示一个请求线程,存了thread(线程)、waitStatus(当前节点的等待状态)、prev(前节点)、next(后节点)。
AQS 组件有哪些?
Semaphore信号量:类似与共享锁,只允许n个线程同时访问共享资源,n可以自己定义。
CountDownLatch倒计时器:也是一种共享锁,允许count个线程阻塞在一个地方,必须等所有线程执行完。
ReentrantLock:可重入锁,
CyclicBarrier:循环栅栏。让一组线程到达一个同步点时被阻塞,直到最后一个线程到达同步点时,所有被屏障拦截的线程才会继续干活。
AQS如何实现公平和非公平锁的?
下面有reentrantLock的源码剖析,总结:
- 公平:
- 非公平:
拓展:AQS为什么采用双向链表?
【Java面试】春招必刷题:AQS为什么采用双向链表?_哔哩哔哩_bilibili
结合源码看看
可以先说双向链表的优势1. 方便进行顺序遍历和反向遍历。2.可以在任意节点以O(1)的复杂度进行插入删除。
- 当双向链表中的线程取消竞争锁时(lockInterruptibly()),需要把改Node从链表中删除,可以以O(1)的时间复杂度找到
preNode并删除当前Node,如果是单向链表则需要从链表头开始遍历复杂度为O(n)。 - 当新加入链表的线程(已经加入链表),进入阻塞状态前 会判断 前驱节点状态,只有前驱节点的waitStatus是-1时 (-1表示前驱有责任唤醒后继节点) 当前线程会阻塞,否则还会尝试竞争资源。这里涉及前驱节点的查找。
CyclicBarrier和CountDownLatch的区别(字节/阿里)
- 概念上的区别,CyclicBarrier通常是多个线程达到同步点被阻塞xxxxx。countdownlatch是调用countDown才会-1,await去等待state为0。
- CyclicBarrier可以循环使用,countdownlatch不行。
Semaphore作用和原理。
CountDownLatch作用和原理、使用场景。
ReentrantLock的实现原理(问得频率不高)
- 可重入的实现,通过state变量。加锁时,如果当前线程等于
ExclusiveOwnerThread,将state+1并放行通过。解锁时,将state变量减一。
- 非公平原理。当持有锁的释放锁时,调用tryRelease,设置
exclusiveOwnerThread为 null ,state=0,并唤醒双向链表中头节点中哨兵指向的第一个Node的线程。
-
- 此时队列中被唤醒线程 和 队列外线程都可以抢占state变量并设置
exclusiveOwnerThread,所以是非公平的。 - 也就是说,非公平锁尝试加锁时,直接CAS对state进行操作,不会检查等待队列中是否有元素。 (有点像插队的感觉)
- 此时队列中被唤醒线程 和 队列外线程都可以抢占state变量并设置
- 公平锁的实现。公平锁尝试加锁(tryAcqiure)时,会先检查AQS链表队列中是否有节点,队列中有节点则不会去CAS竞争锁,而是自觉排队。