Java并发编程之美 第六章 java并发包中锁原理剖析
LockSupport
JDK中的rt.jar包里面的LockSupport是个工具类,主要作用是挂起和唤醒线程
LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport的方法的线程是不持有许可证的
LockSupport使用unsafe类实现
LockSupport的主要函数:
1 park
如果调用park的线程已经拿到与LockSupport关联的许可证,则调用LockSupport.park时会马上返回,否则调用线程会被禁止参与线程的调度,也就是被阻塞
2 unpark
调用unpark时,如果线程没有关联的许可证,则让thread线程持有,
3 parkNanos
抽象同步队列AQS概述
AQS-锁的底层支持
AbstractQueuedSynchronizer抽象同步队列简称AQS,是实现同步器的基础组件,并发包中锁的底层就是使用AQS实现的
AQS是一个FIFO的双向队列,其内部通过节点head和tail记录队首和队尾元素,队列元素的类型为Node
node节点属性:
1 thread 变量存放进入AQS队列的线程
2 SHARED 用来标记该线程是获取共享资源时被阻塞挂起后放入AQS的
3 EXCLUSIVE 是用来标记线程是获取独占资源时被挂起后放入队列的
4 waitStatus 记录当前线程等待状态,CANCELLED(线程被取消了),SIGNAL(线程需要被唤醒),CONDITION(线程在条件队列里面等待),PROPAGATE(释放共享资源时需要通知其他节点)
5 prev 前驱节点
6 next 后继节点
AQS维持了一个单一的状态信息state,其含义如下:
1 ReentrantLock中,state表示当前线程获取锁的可重入次数
2 ReentrantReadWriteLock中,state的高16位表示读状态,也就是获取读锁的次数,低16位表示获取到写锁的线程的可重入次数
3 semaphore中,state表示当前可用信号的个数,
4 countDownLatch中,state表示计数器当前的值
AQS有个内部类ConditionObject,用来结合锁实现线程同步,可以直接访问AQS对象内部的变量,
ConditionObject是条件变量,每个条件变量对应一个条件队列(单向链表队列),用来存放调用条件变量的await方法后被阻塞的线程,
这个条件队列的头尾分别是firstWaiter和lastWaiter
对于AQS来说,线程同步的关键是对状态值state进行操作,根据state是否属于一个线程,操作state的方式分为独占方式和共享方式
1 独占方式
独占资源与具体线程绑定,其他线程尝试获取资源时,会失败后被阻塞
获取与释放资源流程如下:
(1)获取独占资源时,会先tryAcquire方法尝试获取资源,设置state的值,成功则返回,
失败则将当前线程封装为类型node.EXCLUSIVE的node节点插入到阻塞队列的尾部,并调用LockSupport.park方法挂起自己
(2)释放资源时,会tryRelease操作释放资源,设置state值,激活AQS队列里面一个被阻塞的线程
被激活的线程使用tryAcquire,看state是否满足需要,满足就激活,否则还被放入AQS并挂起
2 共享方式
资源与具体线程不相关,多个线程去请求资源时通过CAS方式竞争获取资源
获取与释放资源流程如下:
(1)获取共享资源时,先tryAcquireShared获取资源,设置state的值,成功则返回
失败则封装为node.SHARED的node节点插入AQS阻塞队列的尾部,并调用LockSupport.park挂起自己
(2)释放时,会tryReleaseShared操作释放资源,设置state值,激活AQS队列里面一个被阻塞的线程
被激活的线程使用tryAcquireShared,看state是否满足需要,满足就激活,否则还被放入AQS并挂起
AQS并没有可用的tryAcquire,tryRelease,tryAcquireShared,tryReleaseShared方法,需要由具体的子类实现。
子类根据具体场景使用CAS算法修改state值,
AQS-条件变量的支持
notify和wait,是配合synchronized内置锁实现线程间同步的基础设置
条件变量的signal和await方法也是用来配合锁(使用AQS实现的锁)实现线程间同步的基础设施
synchronized只能与一个变量实现同步
AQS一个锁可以对应多个条件变量
ReentrantLock lock= new ReentrantLock() :创建了一个独占锁 ReentrantLock 对象, ReentrantLock 是基于 AQS 实现的锁
Condition condition = lock . newCondition();
Lock 对象的 newCondition ()方法创建了一个在AQS内部声明的ConditionObject 变量,
这个变量就是 Lock 锁对应的一个条件变量。需要注意的是,一个 Lock 对象可以创 建多个条件变量。
每个条件变量内部维护了一个条件队列,用来存放调用条件变量的await()方法时被阻塞的线程,这个条件队列和AQS队列不一样

独占锁ReentrantLock的原理
ReentrantLock是可重入的独占锁,同时只能有一个线程获取该锁,
AQS的state状态值表示线程获取该锁的可重入次数,
state=0表示没有被线程持有
state=1表示当前线程获取该锁,记录该锁持有者
state=2表示锁持有线程第二次获取该锁
释放时state减1,为0时,表示释放该锁
读写锁ReentrantReadWriteLock的原理
采用读写分离的策略,允许多个线程同时获取读锁
内部维护了一个ReadLock和一个WriteLock,
state的高16位表示读状态,也就是获取到读锁的次数,低16位表示获取到写锁的线程的可重入次数
JDK8中新增的StampedLock锁探究
该锁提供了三种模式的读写控制,当调用获取锁的系统函数时,会返回一个long型的变量,称之为戳记
三种读写模式的锁分别如下:
1 写锁
2 悲观读锁
3 乐观读锁

浙公网安备 33010602011771号