一篇文章让你彻底了解Java可重入锁和不可重入锁

 更新时间:2021年06月01日 17:08:43   作者:Loser_Keep.  
最近正在阅读Java ReentrantLock源码,始终对可重入和不可重入概念理解不透彻,今天特地整理了本篇文章,让你彻底了解Java可重入锁和不可重入锁,需要的朋友可以参考下

可重入锁 

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

我的理解就是,某个线程已经获得某个锁,可以无需等待而再次获取锁,并且不会出现死锁(不同线程当然不能多次获得锁,需要等待)。

简单的说,就是某个线程获得某个锁,之后可以不用等待而再次获取锁且不会出现死锁。

常见的可重入锁

Synchronized和ReentrantLock 都是可重入锁。

可重入锁的释放

同一个线程获取同一个锁,状态值state会累加,假设state累加到了2,每释放一次锁会减1,只有当状态值state减到0了,其他线程才有机会获取锁。也就是说,state归零才是已释放锁的标致。

可重入锁示例

public class ReentrantTest implements Runnable {
 
    @Override
    public void run() {
        get();
    }
 
    public synchronized void get() {
        System.out.println(Thread.currentThread().getName());
        set();
    }
 
    /**
     * 递归方法
     */
    public synchronized void set() {
        System.out.println(Thread.currentThread().getName());
    }
 
    /**
     * 这里的对象锁只有一个,就是rt对象的锁。
     * 当执行rt的get方法时,该线程获得rt对象的锁。在get方法内执行set方法时再次请求rt对象的锁,因为synchronized是可重入锁,所以又可以得到该锁。循环这个过程。
     * 假设不是可重入锁的话,那么请求的过程中会出现阻塞,从而导致死锁。
     * @param args
     */
    public static void main(String[] args) {
        ReentrantTest rt = new ReentrantTest();
        // for(;;)模拟无限循环
        for(;;){
            new Thread(rt).start();
        }
    }
}

分析:这里的对象锁只有一个,就是rt对象的锁。当执行rt的get方法时,该线程获得rt对象的锁。在get方法内执行set方法时再次请求rt对象的锁,因为synchronized是可重入锁,所以又可以得到该锁。循环这个过程。假设不是可重入锁的话,那么请求的过程中会出现阻塞,从而导致死锁。

死锁

多线程中,不同的线程都在等待其它线程释放锁,而其它线程由于一些原因迟迟没有释放锁。程序的运行处于阻塞状态,不能正常运行也不能正常终止。

运行结果

set()和get()同时输出了相同的线程名称,也就是说某个线程执行的时候,不仅进入了set同步方法,还进入了get同步方法。递归使用synchronized也没有发生死锁,证明其是可重入的。

可重入锁的实现原理?

每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增1;当线程退出同步代码块时,计数器会递减1,如果计数器为 0,则释放该锁。

再分析一下上面可重入锁的例子

递归调用一次同步代码块,计数器会变为2,整个递归调用执行完,先退出内层执行减1,再退出外层执行减1。然后释放锁。

不可重入锁

就是某个线程已经获得某个锁,之后不可以再次获取锁,会被阻塞。

设计一个不可重入锁

public class Lock {
    private boolean isLocked = false;
    /**
     * 加锁
     */
    public synchronized void lock() throws Exception{
        while(isLocked){
            //当前线程释放锁,让出CPU,进入等待状态,直到被唤醒,才继续执行15行
            wait();
            System.out.println("wait");
        }
        isLocked = true;
    }
    /**
     * 解锁
     */
    public synchronized void unlock(){
        isLocked = false;
        //唤醒一个等待的线程继续执行
        notify();
    }
}

测试

public class Test {
    Lock lock = new Lock();
    public void print() throws Exception{
        //加锁 标记为true
        lock.lock();
        //释放锁->等待 阻塞在16行
        doAdd();
        lock.unlock();
    }
    public void doAdd() throws Exception{
        lock.lock();
        System.out.println("doAdd");
        lock.unlock();
    }
 
    public static void main(String[] args)throws Exception {
        Test test=new Test();
        test.print();
    }
}

结果:这里,虽然模拟的是不可重入锁,实际还是在单线程环境中的。当前线程执行print()方法首先加锁 标记为true,接下来释放锁->等待 阻塞在16行内部的14行。整个过程中,第一次进入lock同步方法,执行完毕,第二次进入lock同步方法,阻塞等待。这个例子很好的说明了不可重入锁。  

到此这篇关于一篇文章让你彻底了解Java可重入锁和不可重入锁的文章就介绍到这了,更多相关Java可重入锁和不可重入锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot中的自定义FailureAnalyzer详解

    SpringBoot中的自定义FailureAnalyzer详解

    这篇文章主要介绍了SpringBoot中的自定义FailureAnalyzer详解,FailureAnalyzer是一种很好的方式在启动时拦截异常并将其转换为易读的消息,并将其包含在FailureAnalysis中, Spring Boot为应用程序上下文相关异常、JSR-303验证等提供了此类分析器,需要的朋友可以参考下
    2023-12-12
  • 解决Swagger2返回map复杂结构不能解析的问题

    解决Swagger2返回map复杂结构不能解析的问题

    这篇文章主要介绍了解决Swagger2返回map复杂结构不能解析的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 教你怎么用SpringBoot+Mybati-Plus快速搭建代码

    教你怎么用SpringBoot+Mybati-Plus快速搭建代码

    Mybatis自身通过了逆向工程来帮助我们快速生成代码,但Mybatis-plus却更加强大,不仅仅可以生成dao,pojo,mapper,还有基本的controller和service层代码,接下来我们来写一个简单的人门案例是看看如何mybatis-plus是怎么实现的,需要的朋友可以参考下
    2021-06-06
  • Java基础篇之反射机制示例详解

    Java基础篇之反射机制示例详解

    反射是Java可以提供的一个灵活又强大的功能,使用Java反射,您可以知道这个类在运行时具有什么属性和方法,也可以修改属性,调用方法,创建类的实例,这篇文章主要给大家介绍了关于Java基础篇之反射机制的相关资料,需要的朋友可以参考下
    2021-11-11
  • 详解Java中==和equals()的区别

    详解Java中==和equals()的区别

    这篇文章主要介绍了Java中==和equals()的区别,,==可以使用在基本数据类型变量和引用数据类型变量中,equals()是方法,只能用于引用数据类型,需要的朋友可以参考下
    2021-11-11
  • Spring导入properties配置文件代码示例

    Spring导入properties配置文件代码示例

    这篇文章主要介绍了Spring导入properties配置文件代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • 详解JavaEE中Apollo安装使用小结

    详解JavaEE中Apollo安装使用小结

    这篇文章主要介绍了详解JavaEE中Apollo安装与使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • JSON 格式的弊端与解决方法(真实示例)

    JSON 格式的弊端与解决方法(真实示例)

    JSON 格式是目前最流行的数据交互格式,广泛应用于前后端分离的系统。但也有一些场合不适合使用 JSON 格式,这篇文章主要介绍了JSON 格式的弊端与解决方法,需要的朋友可以参考下
    2022-09-09
  • SpringBoot结合Redis实现缓存

    SpringBoot结合Redis实现缓存

    本文主要介绍了SpringBoot结合Redis实现缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • java线程死锁代码示例

    java线程死锁代码示例

    这篇文章主要介绍了java线程死锁代码示例,分享了一个简单线程死锁的例子,需要的朋友可以参考下。
    2017-11-11

最新评论