可重入锁和不可重入锁,递归锁和非递归锁

傷城~ 2022-03-25 11:16 357阅读 0赞

首先引入概念:Java 中15种锁

可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁,

java里面最常见的锁,ReentrantLock和synchronized都是可重入锁

不可重入锁:不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。

如下图设计一个不可重入锁。

  1. public class Lock{
  2. private boolean isLocked = false;
  3. public synchronized void lock() throws InterruptedException{
  4. while(isLocked){
  5. wait();
  6. }
  7. isLocked = true;
  8. }
  9. public synchronized void unlock(){
  10. isLocked = false;
  11. notify();
  12. }
  13. }
  14. public class Test{
  15. Lock lock = new Lock();
  16. <br> /**<br> 调用打印的方法<br> */
  17. public void print(){
  18. lock.lock();
  19. doAdd();
  20. lock.unlock();
  21. }
  22. public void doAdd(){
  23. lock.lock();
  24. //sout("执行业务代码")
  25. lock.unlock();
  26. }
  27. }

场景说明:假设某业务下需要调用Test类里面的print()方法,假设他的线程命名为T0,这时T0会执行lock.lock()方法,首先对于这个对象来说,isLocked属性的初始值时false,因此它进入while循环的时候

判断为false,直接跳出当前循环,把对象的isLocked属性变为true,相当于拿到了锁,这时T0再去执行doAdd(),由于要保证原子性,因此在doAdd方法里面也加入了lock锁,这时,线程还是T0线程,但

由于isLocked属性由于第一次加锁已经变成true,因此,T0线程执行到了wait()方法就处于等待,导致doAdd里面的业务代码无法执行,导致线程阻塞。

下面我们来创建一个可重入锁

  1. public class Lock{
  2. boolean isLocked = false;
  3. Thread lockedBy = null;
  4. int lockedCount = 0;
  5. public synchronized void lock()
  6. throws InterruptedException{
  7. Thread thread = Thread.currentThread();
  8. while(isLocked && lockedBy != thread){
  9. wait();
  10. }
  11. isLocked = true;
  12. lockedCount++;
  13. lockedBy = thread;
  14. }
  15. public synchronized void unlock(){
  16. if(Thread.currentThread() == this.lockedBy){
  17. lockedCount--;
  18. if(lockedCount == 0){
  19. isLocked = false;
  20. notify();
  21. }
  22. }
  23. }
  24. }
  25. public class Test{
  26. Lock lock = new Lock();
  27. /**
  28. 调用打印的方法
  29. */
  30. public void print(){
  31. lock.lock();
  32. doAdd();
  33. lock.unlock();
  34. }
  35. public void doAdd(){
  36. lock.lock();
  37. //sout("执行业务代码")
  38. lock.unlock();
  39. }
  40. }
  41. 场景如上描述,假设线程T0进来了,调用print方法,lock.lock(),第一步首先拿到当前线程,由于初始的islockedfalse,同时lockedbynull 和当前线程T0不相等,false &&true 得到还是false ,因此直接跳出while循环,线程不等待,将isLocked设置为true,同时设置当前锁的数量从0加上1变成1,并且设置lockby为当前线程T0,此时T0继续执行doAdd方法,当执行doAdd()里面的lock.lock()时,同样还是线程T0,因此while循环的判断变成了true&& false,最终拿到的还是false,这时线程还是不等待,isLocked还是true,同时当前线程拥有的锁变成了2lockedby还是T0,这时假设又有T1T2线程进来,当他们执行print()方法,执行到了lock.lock(),首先拿到当前线程是T1,而lockedbyT0,while循环的条件判断是true&&true,则T1就处于了等待状态,只有当T0执行完doAll()的业务代码,并第一次释放锁,lock.unlock(),当前线程的计数器减去1,这时T0再去执行print方法里面的lock.unlock(),这时线程T0,计数器变量变成了0,同时设置isLockedfalse,执行notify方法,唤醒其他的线程,后续线程抢夺资源拿到锁之后,即可实现同步安全的执行。
  42. 总结如下:
  43. 可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
  44. 不可重入锁,也可以叫非递归锁,就是拿不到锁的情况会不停自旋循环检测来等待,不进入内核态沉睡,而是在用户态自旋尝试。
  45. 同一个线程可以多次获取同一个递归锁,不会产生死锁。而如果一个线程多次获取同一个非递归锁,则会产生死锁。

原文地址:https://www.cnblogs.com/edison20161121/p/10293156.html

发表评论

表情:
评论列表 (有 0 条评论,357人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ()

    可重入锁 synchronized 和Lock都是可重入锁,也叫递归锁,即一个线程可以重复获取同一把锁 是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会