首先引入概念:
可重入锁: 广义上的可重入锁指的是可重复可递归调用的锁, 在外层使用锁之后, 在内层仍然可以使用, 并且不发生死锁 (前提得是同一个对象或者 class), 这样的锁就叫做可重入锁,
java 里面最常见的锁, ReentrantLock 和 synchronized 都是可重入锁
不可重入锁: 不可重入锁, 与可重入锁相反, 不可递归调用, 递归调用就发生死锁. 即若当前线程执行某个方法已经获取了该锁, 那么在方法中尝试再次获取锁时, 就会获取不到被阻塞.
如下图设计一个不可重入锁.
- public class Lock{
- private boolean isLocked = false;
- public synchronized void lock() throws InterruptedException{
- while(isLocked){
- wait();
- }
- isLocked = true;
- }
- public synchronized void unlock(){
- isLocked = false;
- notify();
- }
- }
- public class Test{
- Lock lock = new Lock();
- /**
- 调用打印的方法
- */
- public void print(){
- lock.lock();
- doAdd();
- lock.unlock();
- }
- public void doAdd(){
- lock.lock();
- //sout("执行业务代码")
- lock.unlock();
- }
- }
场景说明: 假设某业务下需要调用 Test 类里面的 print() 方法, 假设他的线程命名为 T0, 这时 T0 会执行 lock.lock() 方法, 首先对于这个对象来说, isLocked 属性的初始值时 false, 因此它进入 while 循环的时候
判断为 false, 直接跳出当前循环, 把对象的 isLocked 属性变为 true, 相当于拿到了锁, 这时 T0 再去执行 doAdd(), 由于要保证原子性, 因此在 doAdd 方法里面也加入了 lock 锁, 这时, 线程还是 T0 线程, 但
由于 isLocked 属性由于第一次加锁已经变成 true, 因此, T0 线程执行到了 wait() 方法就处于等待, 导致 doAdd 里面的业务代码无法执行, 导致线程阻塞.
下面我们来创建一个可重入锁
- public class Lock{
- boolean isLocked = false;
- Thread lockedBy = null;
- int lockedCount = 0;
- public synchronized void lock()
- throws InterruptedException{
- Thread thread = Thread.currentThread();
- while(isLocked && lockedBy != thread){
- wait();
- }
- isLocked = true;
- lockedCount++;
- lockedBy = thread;
- }
- public synchronized void unlock(){
- if(Thread.currentThread() == this.lockedBy){
- lockedCount--;
- if(lockedCount == 0){
- isLocked = false;
- notify();
- }
- }
- }
- }
- public class Test{
- Lock lock = new Lock();
- /**
- 调用打印的方法
- */
- public void print(){
- lock.lock();
- doAdd();
- lock.unlock();
- }
- public void doAdd(){
- lock.lock();
- //sout("执行业务代码")
- lock.unlock();
- }
- }
场景如上描述, 假设线程 T0 进来了, 调用 print 方法, lock.lock(), 第一步首先拿到当前线程, 由于初始的 islocked 为 false, 同时 lockedby 为 null 和当前线程 T0 不相等, false &&true 得到还是 false , 因此直接跳出 while 循环, 线程不等待, 将 isLocked 设置为 true, 同时设置当前锁的数量从 0 加上 1 变成 1, 并且设置 lockby 为当前线程 T0, 此时 T0 继续执行 doAdd 方法, 当执行 doAdd() 里面的 lock.lock() 时, 同样还是线程 T0, 因此 while 循环的判断变成了 true&& false, 最终拿到的还是 false, 这时线程还是不等待, isLocked 还是 true, 同时当前线程拥有的锁变成了 2,lockedby 还是 T0, 这时假设又有 T1,T2 线程进来, 当他们执行 print() 方法, 执行到了 lock.lock(), 首先拿到当前线程是 T1, 而 lockedby 是 T0,while 循环的条件判断是 true&&true, 则 T1 就处于了等待状态, 只有当 T0 执行完 doAll() 的业务代码, 并第一次释放锁, lock.unlock(), 当前线程的计数器减去 1, 这时 T0 再去执行 print 方法里面的 lock.unlock(), 这时线程 T0, 计数器变量变成了 0, 同时设置 isLocked 为 false, 执行 notify 方法, 唤醒其他的线程, 后续线程抢夺资源拿到锁之后, 即可实现同步安全的执行.
总结如下:
可重入锁, 也叫做递归锁, 指的是同一线程 外层函数获得锁之后 , 内层递归函数仍然有获取该锁的代码, 但不受影响.
不可重入锁, 也可以叫非递归锁, 就是拿不到锁的情况会不停自旋循环检测来等待, 不进入内核态沉睡, 而是在用户态自旋尝试.
同一个线程可以多次获取同一个递归锁, 不会产生死锁. 而如果一个线程多次获取同一个非递归锁, 则会产生死锁.
来源: https://www.cnblogs.com/edison20161121/p/10293156.html