目录
上一篇整理,知道了synchronized的使用,以及synchronized的原理,还有管程
管程就是通过加锁来保证并发安全,用的比较多的,就是MESA
MESA内部存在同步等待队列和许多的条件等待队列,获取锁失败的线程会进入同步等待队列
而synchronized是JVM层面对管程的实现,是基于对象的,通过ObjectMonitor来实现,获取锁失败的线程,会进入到cxq队列,这就是同步等待队列,而wait,notify,notifyAll,就是条件等待队列
但是synchronized也有缺点:自动加锁解锁,不支持手动解锁;实现比较重
这是synchronized的缺点,并且没办法改变
那,在保证性能的前提下,Java层面该怎么实现管程,于是开发大佬就搞出来了接下来的东西
并发这块,用的很多的锁,除了synchronized,很多人都会想到ReentrantLock,而ReentrantLock也是有基础的,那就是基于AQS
AQS是什么,就是AbstractQueuedSynchronizer的简写
java.util.concurrent包中大多数的同步器实现,如Lock、Latch、Barrier,都是围绕着一个共同的基本行为,比如等待队列、条件队列、独占获取、共享资源等等,这些行为的抽象就是基于AQS实现的,AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器
AQS具备如下特性:
volatile int state
state表示资源的可用状态,访问state,有三种方式
getState(), setState(), compareAndSetState()
AQS定义了两种资源共享方式
AQS有两种队列,分别是同步等待队列和条件等待队列
同步等待队列主要用于维护获取互斥锁失败时入对的线程
条件等待队列主要用于,线程调用await()方法时释放锁,进入条件队列,等调用了signal()方法唤醒的时候,会把条件队列中的线程节点移动到同步队列,等待重新获取锁
AQS定义了五种线程在队列中的节点状态
不同的自定义同步器竞争共享资源的方式不同,自定义同步器在实现时只需要实现共享资源state的获取与释放就可以,至于具体的线程等待队列的维护,比如资源获取失败入队,唤醒出队等等,AQS已经在最顶层实现好了,自定义同步器实现时主要实现以下几种方法
AQS中的同步等待队列,也称为CLH队列,是一种基于双向链表结构的队列,是一种先入先出的等待队列,AQS依赖于CLH队列来完成对同步状态的管理,至于为什么叫CLH队列,那是因为,CLH队列是Craig、Landin、Hagersten三人发明的
如果当前线程获取同步状态失败,AQS会把当前线程已经等待状态等信息构造成一个节点,并将其加入到CLH队列,同时会阻塞当前线程
当同步状态被释放时,会唤醒首节点(公平锁),使其在此尝试获取同步状态
通过signal或者signalAll可以将条件队列中的节点转移到同步队列
AQS中条件等待队列是用单向列表保存的,用nextWaiter来拼接
调用await()方法阻塞线程
当前线程存在于同步队列的头节点,调用await()方法进行阻塞,从而就由同步队列转换为了条件队列
可以将Condition通俗的理解为条件队列
调用Condition#await方法会释放当前持有的锁,然后阻塞当前线程,同时向Condition队列尾部添加一个节点,所以调用Condition#await方法的时候必须持有锁
调用Condition#signal方法会将Condition队列的首节点移动到阻塞队列尾部,然后唤醒因调用Condition#await方法而阻塞的线程(唤醒之后这个线程就可以去竞争锁了),所以调用Condition#signal方法的时候必须持有锁,持有锁的线程唤醒被因调用Condition#await方法而阻塞的线程