LookSupport工具类
JDK中的 rt.jar 包里面的 LockSupport 是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。
park()方法–阻塞
如果调用 park 方法的线程已经拿到了与 LockSupport 关联的许可证 ,则 调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
unpark(Thread thread)方法–唤醒
当一个线程调用 unpark 时,如果 参数 thread 线程没有持有 thread 与 LockSupport 类关联的许可证, 则让 thread 线程持有。 如果 thread 之前因调用 park()而被挂起,则调用 unpark 后,该线程会被唤醒。如果 thread 之前没有调用 park,则调用 unpark 方法后, 再调用 park 方法,其会立刻返回 。
AQS概述

state
AQS 中维持了一个单一的状态信息state,可以通过 getState 、setState 、 compareAndSetState 函数修改其值 。
- state用volatile修饰,保证多线程中的可见性。
- getState()和setState()方法采用final修饰,限制AQS的子类重写它们两。
- compareAndSetState()方法采用乐观锁思想的CAS算法,也是采用final修饰的,不允许子类重写。
对于ReentrantLock的实现来说,state可以用来表示当前线程获取锁的可重入次数;
对于读写锁ReentrantReadWriteLock来说,state的高16位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数;
对于semaphore来说,state用来表示当前可用信号的个数:
对于CountDownlatch来说,state用来表示计数器当前的值。
CLH队列

CLH(Craig, Landin, and Hagersten locks) 同步队列 是一个FIFO双向队列,其内部通过节点head和tail记录队首和队尾元素,队列元素的类型为Node。
AQS依赖它来完成同步状态state的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
Node
Node节点内部的SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的,EXCLUSIVE用来标记线程是获取独占资源时被挂起后放入AQS队列的;
waitStatus记录当前线程等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点);
prev记录当前节点的前驱节点,next记录当前节点的后继节点。


独占or共享
在独占方式下,获取与释放资源的流程如下:
- 当一个线程调用acquire(intarg)方法获取独占资源时,会首先使用tryAcquire方法尝试获取资源,具体是设置状态变量state的值,成功则直接返回,失败则将当前线程封装为类型为Node.EXCLUSIVE的Node节点后插入到AQS阻塞队列的尾部,并调用LockSupport.park(this)方法挂起自己。
1 | public final void acquire(int arg) { |
- 当一个线程调用release(intarg)方法时会尝试使用tryRelease操作释放资源,这里是设置状态变量state的值,然后调用LockSupport.unpark(thread)方法激活AQS队列里面被阻塞的一个线程(thread)。被激活的线程则使用tryAcquire尝试,看当前状态变量state的值是否能满足自己的需要,满足则该线程被激活,然后继续向下运行,否则还是会被放入AQS队列并被挂起。
1 | public final boolean release(int arg) { |
**AQS类并没有提供可用的tryAcquire和tryRelease方法,需要由具体的子类来实现。**子类在实现tryAcquire和tryRelease时要根据具体场景使用CAS算法尝试修改state状态值,成功则返回true,否则返回false。
和独占方法类似,共享方式下则是acquireShared(int arg),releaseShared(int arg)。
条件变量ConditionObject
类似于notify和wait,ConditionObject的signal和await方法也是用来配合锁(使用 AQS 实现的锁)实现线程间同步的基础设施。
ReentrantLock原理

ReentrantLock最终还是使用AQS来实现的,并且根据参数来决定其内部是一个公平还是非公平锁,默认是非公平锁。
jdk8中ReentrantLock的公平锁实现,主要是重写了tryAcquire方法,判断队列是否为空,队列为空直接获得锁,否则放入同步队列中等待。
jdk8中ReentrantLock的非公平锁实现,NonfairSync类调用了父类Sync的nonfairTryAcquire方法,如果当前锁状态为0,会先去尝试获取锁,获取失败才会加入等待队列中。
1 |
|
1 | /** |

ReentrantReadWriteLock原理
ReentrantReadWriteLock原理和ReentrantLock差不多,同样支持公平和非公平锁。
ReentrantReadWriteLock巧妙地使用AQS的状态值的高 16 位表示获取到读锁的个数,低16位表示获取写锁的线程的可重入次数,并通过CAS对其进行操作实现了读写分离,这 在读多写少的场景下比较适用。
读锁不支持条件队列


StampedLock锁
StampedLock是并发包里面JDK8版本新增的一个锁,该锁提供了三种模式的读写控制。
当调用获取锁的系列函数时,会返回一个long型的变量,我们称之为戳记(stamp),这个戳记代表了锁的状态。
其中try系列获取锁的函数,当获取锁失败后会返回为0的stamp值。
当调用释放锁和转换锁的方法时需要传入获取锁时返回的stamp值。
StampedLock的读写锁都是不可重入锁,所以在获取锁后释放锁前不应该再调用会获取锁的操作,以避免造成调用线程被阻塞。