可重入锁(递归锁)

Love The Way You Lie 2022-09-11 10:29 214阅读 0赞

可重入锁

synchronized 和Lock都是可重入锁,也叫递归锁,即一个线程可以重复获取同一把锁

是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

synchronized 是隐式锁,Lock是显式锁

即synchronized 加锁解锁自动完成

Lock加锁解锁要手动完成

synchronized

synchronized 可重入锁(同步代码块)

  1. package com.dongguo.sync;
  2. /** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description:synchronized 可重入锁 */
  3. public class SyncLockDemo {
  4. public static void main(String[] args) {
  5. Object object = new Object();
  6. new Thread(() -> {
  7. synchronized (object) {
  8. System.out.println(Thread.currentThread().getName() + "-外层");
  9. synchronized (object) {
  10. System.out.println(Thread.currentThread().getName() + "-中层");
  11. synchronized (object) {
  12. System.out.println(Thread.currentThread().getName() + "-内层");
  13. }
  14. }
  15. }
  16. }, "ThreadA").start();
  17. }
  18. }
  19. 运行结果:
  20. ThreadA-外层
  21. ThreadA-中层
  22. ThreadA-内层

synchronized 递归锁(同步方法)

  1. package com.dongguo.sync;
  2. /** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: synchronized 可重入锁 */
  3. public class SyncLockDemo {
  4. public static void main(String[] args) {
  5. new SyncLockDemo().add();
  6. }
  7. public long number = 0;
  8. //递归
  9. public synchronized void add(){
  10. number++;
  11. System.out.println(number);
  12. add();//自己调自己
  13. }
  14. }

image-20210903210953346

Synchronized的重入的实现原理

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

ReentrantLock

image-20210913150747857

ReentrantLock可重入锁

ReentrantLock相对于 synchronized 它具备如下特点
可中断
可以设置超时时间
可以设置为公平锁
支持多个条件变量
与 synchronized 一样,都支持可重入

  1. package com.dongguo.sync;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: Lock 可重入锁 */
  5. public class SyncLockDemo {
  6. public static void main(String[] args) {
  7. Lock lock = new ReentrantLock();
  8. new Thread(() -> {
  9. try {
  10. lock.lock();
  11. System.out.println(Thread.currentThread().getName() + "-外层");
  12. try {
  13. lock.lock();
  14. System.out.println(Thread.currentThread().getName() + "-中层");
  15. try {
  16. lock.lock();
  17. System.out.println(Thread.currentThread().getName() + "-内层");
  18. } finally {
  19. lock.unlock();
  20. }
  21. } finally {
  22. lock.unlock();
  23. }
  24. } finally {
  25. lock.unlock();
  26. }
  27. }, "ThreadA").start();
  28. }
  29. }
  30. 运行结果:
  31. ThreadA-外层
  32. ThreadA-中层
  33. ThreadA-内层

注意:加锁几次就要解锁几次

  1. package com.dongguo.sync;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: */
  5. public class SyncLockDemo1 {
  6. public static void main(String[] args) {
  7. Lock lock = new ReentrantLock();
  8. new Thread(() -> {
  9. try {
  10. lock.lock();
  11. System.out.println(Thread.currentThread().getName() + "-外层");
  12. try {
  13. lock.lock();
  14. System.out.println(Thread.currentThread().getName() + "-中层");
  15. try {
  16. lock.lock();
  17. System.out.println(Thread.currentThread().getName() + "-内层");
  18. } finally {
  19. lock.unlock();
  20. }
  21. } finally {
  22. //lock.unlock(); //这里故意注释,实现加锁次数和释放次数不一样
  23. //由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
  24. }
  25. } finally {
  26. lock.unlock();
  27. }
  28. }, "t1");
  29. new Thread(() -> {
  30. try {
  31. lock.lock();
  32. System.out.println("t2获得lock锁");
  33. } finally {
  34. lock.unlock();
  35. }
  36. }, "t2");
  37. }
  38. }

image-20210906095443516

由于t1获取锁与释放锁的次数不同,t2无法获得lock锁

ReentrantLock递归锁

  1. package com.dongguo.sync;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: */
  5. public class SyncLockDemo {
  6. Lock lock = new ReentrantLock();
  7. public static void main(String[] args) {
  8. new SyncLockDemo().add();
  9. }
  10. public long number = 0;
  11. //递归
  12. public void add(){
  13. try {
  14. lock.lock();
  15. number++;
  16. System.out.println(number);
  17. add();//自己调自己
  18. } finally {
  19. lock.unlock();
  20. }
  21. }
  22. }

运行结果:

image-20210903211958954

发表评论

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

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

相关阅读

    相关

              可重入锁的设计目的是为了防止死锁。例如在多个方法互相调用的场景,需要保证可重入,不然很容易死锁。           java中的synchronized

    相关 ()

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

    相关 与非

    可重入锁与非可重入锁 可重入锁又称递归锁,是指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获