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 乐观读锁

 

posted @ 2019-07-22 17:51  褐色键盘  阅读(196)  评论(0)    收藏  举报