并发编程:不得不说的ReentrantLock
Posted fcb-it
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程:不得不说的ReentrantLock相关的知识,希望对你有一定的参考价值。
从Lock讲起
Lock:一个接口,定义了在jdk层面上灵活实现锁的一种方式。
实现该接口的类是ReentrantLock。ReentrantLock这个单词的翻译是重入锁。
重入锁
持有锁的线程可以再次获取锁,增加重入次数,释放的锁时候也要将次数减为0。synchronized和ReentrantLock都是重入锁。
ReentrantReadWriteLock:重入读写锁,读和读可以共享,读和写、写和写存在互斥关系。
ReentrantLock的原理
加锁流程
一开始线程A调用lock方法抢占锁会通过CAS更新锁的状态State字段(0,1),假如更新成功则认为获取到了锁 --->
此时某个线程(可能是A自身)又调用了lock方法,此时会先去查看state:
假如等于0则直接去CAS抢锁;
不等于0,且当前线程已经是占有锁的线程,重入一次state+1;
不等于0,当前线程也没有持有锁---->
把当前节点封装成EXCLUSIVE NODE,初次加入链表时 自旋加入链表(先初始化一个空的头结点,并设置head和tail都指向该节点,初始化成功或者没初成功的线程 都去自旋更新尾节点);
封装到链表以后,假如prev是头结点,则再次尝试获取锁,获取失败后将前置节点的状态改为SIGNAL (-1),如果不是SIGNAL会自旋一次,假如是SIGNAL,挂起线程;
解锁流程
CAS将state - 1,假如state减到0了(因为重入可能需要释放多次),表示真正地释放锁,并唤醒后一个节点。唤醒时会将当前节点status从SIGNAL改成0,此时后面的线程假如抢到了锁,会执行之前自旋获得锁方法后续部分---> 将头结点指向自己,将旧的头结点释放(old.next = null),并将当前节点变成空节点。此时就完成借节点的释放。
interrupt
注意在加锁和解锁的过程中,其他线程调用了被挂起线程的interrupt方法,被挂起的线程是无法立即响应的,该线程被唤醒后会查看interrupt状态,假如被interrupt过,就在之后调用线程的interrupt方法。
ReentrantLock和synchronize的区别
- ReentrantLock是JUC包提供给我们的锁,synchronize是jvm级别的关键字
- 释放:synchronize同步块执行结束或者抛异常;ReentrantLock调用unlock()方法
- ReentrantLock可以灵活地控制锁,提供了公平锁
- ReentrantLock有tryLock方法在阻塞时可以中断
- ReentrantLock条件等待更灵活
以上是关于并发编程:不得不说的ReentrantLock的主要内容,如果未能解决你的问题,请参考以下文章