一、介绍
1.1 简介
在Java1.5,Java并发包提供的一个实现Lock接口的可重入锁【ReentrantLock】, 内部采用AbstractQueuedSynchronizer 实现, 支持获取锁时的公平和非公平性选择。
1.2 相关概念
- 可重入锁:指的是同一线程外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码。也就是对于相同的锁,同一线程可以获得多次。不然就会发生死锁问题。
- 可中断锁:在某些条件下可以相应中断的锁。在Java中,synchronized就不是可中断锁,而 Lock是可中断锁。
- 公平锁:公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。
- 非公平锁:无法保证锁的获取是按照请求锁的顺序进行的。有可能锁刚被释放,正好新来了个线程请求锁,这样后面等待的线程就不能获得锁。在极端情况下,可能导致一直无法获取锁。
注意: 对于非公平锁,这里我一度以为每次争取锁的过程都是非公平的。 其实不然,如果已在队列中排队的线程,是按照FIFO 顺序获取锁,但这是如果有一个新的线程来争取锁,那么在非公平锁模式下,这个新的线程可能会比在等待队列中排队的线程先获取到锁。
二、获取锁过程
2.1 获取锁流程图
获取锁的整体流程
非公平锁和公平锁的tryAcquire实现细节
2.2 ReentrantLock 内部结构
三个线程开始争取锁,因为是不公平锁,所以可以同时争取锁
当线程2获取到锁后,其它线程通过cas加入等待队列。自旋等待获取锁
如果线程2没有释放锁,再次访问共享资源时,因为当前占用锁的线程是线程2,所以只需要设置state加一
三、释放锁过程
3.1 释放锁流程

3.2 释放锁时AQS的变化
在线程2 释放锁后,队列中的线程在自旋中判断锁是否已释放,如果已释放且线程所在节点是头节点的下一个节点(如线程1),即可以获取锁。
四、Condition 条件锁
Java 官方提供的Condition例子
详情查看
public class BoundedBufferTest {//锁final Lock lock = new ReentrantLock();//队列已满条件final Condition notFull = lock.newCondition();//队列已空条件final Condition notEmpty = lock.newCondition();//队列容器final Object[] items = new Object[10];//下标,和计数int putptr, takeptr, count;public void put(Object x) throws InterruptedException {//上锁lock.lock();try {//如果当前数量count 等于容器最大容量while (count == items.length)//挂起写线程notFull.await();items[putptr] = x;if (++putptr == items.length) putptr = 0;++count;//唤醒读线程notEmpty.signal();} finally {lock.unlock();}}public Object take() throws InterruptedException {lock.lock();try {//当前数量count等于0while (count == 0)//挂起读线程notEmpty.await();Object x = items[takeptr];if (++takeptr == items.length) takeptr = 0;--count;//唤醒写线程notFull.signal();return x;} finally {lock.unlock();}}public static void main(String[] args) {BoundedBufferTest boundedBufferTest = new BoundedBufferTest();new Thread(() -> {try {while (true) {int i = new Random().nextInt(10000);System.out.println("send =>" + i);boundedBufferTest.put(i);TimeUnit.SECONDS.sleep(1);}} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {while (true) {Object take = boundedBufferTest.take();System.out.println("<= receive " + take);TimeUnit.SECONDS.sleep(5);}} catch (InterruptedException e) {e.printStackTrace();}}).start();}}
4.1 await() 阻塞过程
- 整体流程图

- 锁内部结构
初始化时,内部结构
当线程开始运行时, 线程read 获取到锁,因为队列容器中没有元素,进而挂起线程read, 【notEmpty.await()】
当线程read成功释放锁后,线程write开始获取锁, 往容器中put元素
4.2 signal()唤醒过程
- 流程图

- 锁内部结构
参考
- https://www.processon.com/view/5df335fde4b004cc9a2dfa70
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html
- https://www.processon.com/special/template/58613652e4b032b0452c6e3a
- https://www.cnblogs.com/liqiangchn/p/12081838.html
