2022年1月17日
什么是AQS?
AQS(AbstractQueuedSynchronizer)是一个抽象同步框架,可以用来实现一个依赖状态的同步器。
AQS具备的特性:
- 阻塞等待队列
- 共享/独占
- 公平/非公平
- 可重入
- 允许中断
AQS内部维护属性volatile int state
- state表示资源的可用状态
State三种访问方式:
- getState()
- setState()
- compareAndSetState()
AQS定义两种资源共享方式
- Exclusive-独占,只有一个线程能执行,如ReentrantLock
- Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
AQS定义两种队列
- 同步等待队列: 主要用于维护获取锁失败时入队的线程
- 条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁
AQS 定义了5个队列中节点状态:
- 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
- CANCELLED,值为1,表示当前的线程被取消;
- SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
- CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
- PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
条件等待队列
AQS中条件队列是使用单向列表保存的,用nextWaiter来连接:
- 调用await方法阻塞线程;
- 当前线程存在于同步队列的头结点,调用await方法进行阻塞(从同步队列转化到条件队列)
ReentrantLock设计精髓
- ReentrantLock加锁解锁的逻辑
- 公平和非公平,可重入锁的实现
- 线程竞争锁失败入队阻塞逻辑和获取锁的线程释放锁唤醒阻塞线程竞争锁的逻辑实现 ( 设计的精髓:并发场景下入队和出队操作)
问题:
- ReentrantLock公平锁和非公平锁的性能谁更高?
非公平锁性能更高,相比更少的上下文切换,带来更高的性能
- ReentrantLock中tryLock()和lock()方法的区别
获取锁和加锁的区别
- ReentrantLock中的公平锁和非公平锁的底层实现
公平锁直接放入队列了,非公平锁会先尝试获取锁,获取不到再放入队列
- Sychronized和ReentrantLock的区别
Sychronized是JVM层面实现,ReentrantLock是Java实现;
ReentrantLock提供了多样化的同步,在激烈的并发性能能维持常态,Sychronized在竞争不激烈的情况下性能较好,在激烈并发下性能下降厉害;
Sychronized先自旋实在不行再上锁,ReentrantLock是直接锁住,带来较多的上下文切换消耗;
两者都支持非公平锁
- 线程池中阻塞队列的作用?为什么是先添加列队而不是先创建最大线程
一般队列只能保证一个有效的长度,超过长度后就无法保留了,阻塞队里可以通过阻塞保留继续入队的任务,阻塞队列自带阻塞和唤醒功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一直占有cpu资源。
线程池最大的好处是尽可能的复用线程,减少开销,而且创建线程需要切换到内核态,锁住其他线程,影响其他线程