可重入锁和不可重入锁

不可重入锁

先来设计一种锁

复制代码
public class Lock{
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException{
        while(isLocked){    
            wait();//把当前线程wait
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();
    }
}
复制代码

这其实是个不可重入锁,举个例子

复制代码
public class Count{
    Lock lock = new Lock();
    public void print(){
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd(){
        lock.lock();
        //do something
        lock.unlock();
    }
}
复制代码

  当调用print()方法时,获得了锁,这时就无法再调用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){//获得该锁的那个线程,获得了多少次该锁(即调用了几次lock方法,即重入了几次),就得unlock几次,即lockedCount=0,才会把那些wait(阻塞)的线程唤醒
                isLocked = false;
                notify();
            }
        }
    }
}
复制代码

相对来说,可重入就意味着:一个线程可以进入任何一个 该线程 已经拥有的锁所同步着的代码块

  第一个线程执行print()方法,得到了锁,使lockedBy等于当前线程,也就是说,执行的这个方法的线程获得了这个锁,执行add()方法时,同样要先获得锁(即调用lock.lock()),因不满足while循环的条件isLocked(=true,因为该线程调用print()方法时就获得该锁了);但是这里与不可重入锁的区别是有个lockedBy(即表示现在哪个线程持有锁);因为调用add()方法的是和调用print()的是同一个线程,也就是不等待,继续进行,将此时的lockedCount变量,也就是当前获得锁的数量加一,当释放了所有的锁(即得调用获得锁数量次数的unlock),才执行notify()。

  如果在执行这个方法时,有第二个线程想要执行这个方法,因为lockedBy不等于第二个线程,导致这个线程进入了循环,也就是等待(阻塞),不断执行wait()方法。只有当第一个线程释放了所有的锁(即第一个线程调用了多少次lock()方法就得调用多少次unlock()方法释放锁),执行了notify()方法,第二个线程才得以跳出循环,继续执行。

这就是可重入锁的特点。

  可重入锁与不可重入锁对比,简单来说就是:可重入锁会多两个属性(1、获得该锁的线程;2、获得该锁的次数),根据第一个属性判断,如果是持有该锁的那个线程又来lock,不会被阻塞(wait),而是在上锁的次数加一(表示这个线程又锁了一次(重入)),只有该线程unlock的次数达到上锁的次数(即第二个属性等于0),才会唤醒其他线程。

 

java中常用的可重入锁:

synchronized

java.util.concurrent.locks.ReentrantLock

 

https://www.cnblogs.com/dj3839/p/6580765.html

posted @   xdyixia  阅读(9263)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示