多线程锁:可重入锁(又名递归锁)

可重入锁允许在同一个线程中多次进入同步代码块或方法,防止因递归调用导致线程阻塞,是Java的ReentrantLock和synchronized的特点。这种锁机制能减少死锁的发生。文章通过隐式锁Synchronized和显式锁Lock的示例,解释了可重入锁的工作原理和重入实现机理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

可重入锁

四个字分开来解释

可重入锁种类(隐式锁,显示锁)

隐式锁Synchronized

Synchronized的重入实现机理 

 显式锁Lock


可重入锁

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

如果是1个有 synchronized 修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚。

所以Java中ReentrantLock和synchronized都是可重入锁

可重入锁的一个优点是可一定程度避免死锁

四个字分开来解释

  • 可:可以

  • 重:再次

  • 入:进入

  • 锁:同步锁

  • 进入什么:进入同步域(即同步代码块/方法或显示锁锁定的代码)

  • 一句话:一个线程中的多个流程可以获取同一把锁,持有这把锁可以再次进入。自己可以获取自己的内部锁。

可重入锁种类(隐式锁,显示锁)

隐式锁Synchronized

同步代码块中演示

 public static void main(String[] args) {
       final Object obj = new Object();

       new Thread(()->{
           synchronized (obj){
               System.out.println(Thread.currentThread().getName()+"外层");
               synchronized (obj){
                   System.out.println(Thread.currentThread().getName()+"中层");
                   synchronized (obj){
                       System.out.println(Thread.currentThread().getName()+"内层");
                   }
               }
           }
       }).start();

    }

三个同步代码块持有同一个锁——obj对象

Thread-0外层
Thread-0中层
Thread-0内层

同步方法中演示 

public synchronized void m1() {
        System.out.println("m1 come in...");
        m2();
        System.out.println("m1 end...");
    }

    private synchronized void m2() {
        System.out.println("m2 come in...");
        m3();
    }

    private synchronized void m3() {
        System.out.println("m3 come in...");
    }

    public static void main(String[] args) {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        new Thread(()->{reentrantLockDemo.m1();},"t1").start();

    }

 三个同步方法持有同一个锁——当前类对象reentrantLockDemo

m1 come in...
m2 come in...
m3 come in...
m1 end...

Synchronized的重入实现机理 

底层ObjectMoitor.hpp中记录

140行
  ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //用来记录该线程获取锁的次数
    _waiters      = 0,
    _recursions   = 0;//锁的重入次数
    _object       = NULL;
    _owner        = NULL; //------最重要的----指向持有ObjectMonitor对象的线程,记录哪个线程持有了我
    _WaitSet      = NULL; //存放处于wait状态的线程队列
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;//存放处于等待锁block状态的线程队列
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

ObjectMoitor.hpp底层:每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

_count //用来记录该线程获取锁的次数

_owner//------最重要的----指向持有ObjectMonitor对象的线程,记录哪个线程持有了我

  1. 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
  2. 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
  3. 当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
     

 显式锁Lock

public class ObviousLock {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        new Thread(()->{
            lock.lock();
            try {
                System.out.println("外层进入");
                lock.lock();
                try {
                    System.out.println("内层进入");
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }).start();
    }
}

注意:lock unlock要成对使用

假如lock unlock不成对,单线程情况下问题不大,但多线程下出问题

public class ObviousLock {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        new Thread(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+" 外层进入");
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName()+" 内层进入");
                } finally {
                    lock.unlock();
                }
            } finally {
//                lock.unlock();
            }
        },"t1").start();

        new Thread(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"外层进入");
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName()+"内层进入");
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        },"t2").start();


    }
}

//t1 外层进入
//t1 内层进入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜sss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值