AQS笔记&循环依赖

​LockSupport是用来创建锁或其它同步类的基本线程阻塞原语

LockSupport类用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit)

permit只有两个值1和0,默认是0

可以把permit看成是一种(0,1)信号量,但与semaphore不同的是,许可的累加上线是1.

API方法:

park(阻塞)

unpark(唤醒)

使用方法:

LockSupport.park()

LockSupport.unpark(thread)

 

LockSupport的所有方法都是静态的,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法,归根结底,LockSupport调用的Unsafe的native代码。

 

permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1,park方法会被唤醒。然后将park()方法再次设置为0并返回。

AQS

AbstractQueueSynchronizer

抽象队列同步器

是用来构建锁或其它同步组件的重量级基础框架,及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态

 

和AQS有关的:ReetrantLock、CountDownLatch、ReetrantReadWriteLock、Semphore、CyclicBarrier

ReetrantLock的Sync extends AbstractQueueSynchronizer

CountDownLatch的Sync extends AbstractQueueSynchronizer

ReetrantReadWriteLock的Sync extends AbstractQueueSynchronizer

Semphore的Sync extends AbstractQueueSynchronizer

 

锁与同步器的关系:

锁:面向锁的使用者,定义了程序员和锁交互的使用层API,隐藏了实现细节,你调用即可

同步器:面向锁的实现者,DougLee提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制。

 

为什么需要AQS?

加锁会导致阻塞。

 

抢占到资源的线程直接使用处理逻辑,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,暂时没有顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)

既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么样的数据结构呢?

如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象实现。它请求共享资源的线程封装成队列的节点(Node),通过CAS、自旋以及LockSupport.part()的方式,维护state变量的状态,使并发达到同步的控制效果。

 

AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改。

AQS Node<Thread>

hashmap Node<K,V>

AQS=State变量+CLH双端队列+Node<Thread>

Node 同尾部入队,头部出队,通过自旋等待。

 

Lock的lock和unlock都是聚合了一个AQS的子类实现的。

 

spring循环依赖

对构造注入不友好,对setter注入友好

spring对循环依赖的单例bean不报错,原型bean循环依赖会报错。

DefaultSingletonBeanRegistry

spring通过三级缓存hashmap来处理循环依赖

 

是否可以关闭循环依赖?

为什么使用三级缓存?使用二级缓存可以吗?使用一级缓存可以吗?

posted @ 2021-06-07 17:56  gavin苗  阅读(97)  评论(0)    收藏  举报