究竟什么是可重入锁?

究竟什么是可重入锁?经历很久之前就听说了可重入锁,可重入锁究竟是什么意思,以前是囫囵吞枣的,只要记住ReentrantLock和sychronized是可重入锁就行了,爱咋用咋用,好吧,原谅我的无知,最近对基础查漏补缺,发现竟然对其一问三不知,赶紧预习一波,觉得有必要写一篇博客来讲解,就当做什么都没有发生吧,嘿嘿。。。释义广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(

大家好,又见面了,我是你们的朋友全栈君。

经历

很久之前就听说了可重入锁,可重入锁究竟是什么意思,以前是囫囵吞枣的,只要记住ReentrantLock和sychronized是可重入锁就行了,爱咋用咋用,好吧,原谅我的无知,最近对基础查漏补缺,发现竟然对其一问三不知,赶紧预习一波,觉得有必要写一篇博客来讲解,就当做什么都没有发生吧,嘿嘿。。。

释义

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

public class ReentrantTest implements Runnable { 
   

    public synchronized void get() {
        System.out.println(Thread.currentThread().getName());
        set();
    }

    public synchronized void set() {
        System.out.println(Thread.currentThread().getName());
    }

    public void run() {
        get();
    }

    public static void main(String[] args) {
        ReentrantTest rt = new ReentrantTest();
        for(;;){
            new Thread(rt).start();
        }
    }
}

整个过程没有发生死锁的情况,截取一部分输出结果如下:

Thread-8492
Thread-8492
Thread-8494
Thread-8494
Thread-8495
Thread-8495
Thread-8493
Thread-8493

set()和get()同时输出了线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。

不可重入锁

不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。看到一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();

    public void lock() {
        Thread current = Thread.currentThread();
        //这句是很经典的“自旋”语法,AtomicInteger中也有
        for (;;) {
            if (!owner.compareAndSet(null, current)) {
                return;
            }
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

代码也比较简单,使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的,而实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。稍微改一下,把它变成一个可重入锁:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

    private AtomicReference<Thread> owner = new AtomicReference<Thread>();
    private int state = 0;

    public void lock() {
        Thread current = Thread.currentThread();
        if (current == owner.get()) {
            state++;
            return;
        }
        //这句是很经典的“自旋”式语法,AtomicInteger中也有
        for (;;) {
            if (!owner.compareAndSet(null, current)) {
                return;
            }
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        if (current == owner.get()) {
            if (state != 0) {
                state--;
            } else {
                owner.compareAndSet(current, null);
            }
        }
    }
}

在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。

ReentrantLock中可重入锁实现

这里看非公平锁的锁获取方法:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //就是这里
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

在AQS中维护了一个private volatile int state来计数重入次数,避免了频繁的持有释放操作,这样既提升了效率,又避免了死锁。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153266.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【032】JavaScript 计算笛卡尔积[通俗易懂]

    【032】JavaScript 计算笛卡尔积[通俗易懂]这是一个用JavaScript实现笛卡尔积的例子。注意:本文中所说的集合是指数学上的集合,不是es6里的Set。整体思路如下:用户传入一个二维的数组,每个子数组都是一个要进行笛卡尔积计算的集合。返回一个二维数组,每个子数组都是一个有序对或者n元有序组。当用户传入一个集合的时候,为了兼容业务,返回形如:[[a],[b]…]的二维数组。当用户只传入两个集合计算的时候,普通的嵌套计算即可。当用

    2022年7月11日
    14
  • C++ map 根据value找key、 根据key找value

    C++ map 根据value找key、 根据key找value根据value找key    有可能找到多个结果根据key找value    、、、、、、运行效果:代码很简单,如下:#include&lt;iostream&gt;#include&lt;map&gt;#include&lt;string&gt;usingnamespacestd;intmain(intargc,char*…

    2022年7月23日
    9
  • 免费网站源码分享平台 有哪些好的源码网站

    免费网站源码分享平台 有哪些好的源码网站有哪些值得推荐的源码共享网站网站源码资源当然首选站长源码下载了,主要源码安全系数要高点,最主要是免费,还有就是一些商业源码分享站了可能会要积分才能下载了,比如商业源码,A5源码,源码…有没有好用的免费网站源码网站?不知道你的目的是什么,目前一般网站建设都是用cms,做好前台就好,你先要看你选择什么cms,然后可以根据这个选什么样的模板。如果谈开发的话那就是Github.求有源码分享的网站如果是JAVA,需要javaDemo.可以看看这个,最代码是一个垂直于国内java

    2022年7月20日
    22
  • Flutter 使用插件打开相册、相机「建议收藏」

    Flutter 使用插件打开相册、相机「建议收藏」需求:image_picker的使用,点击按钮底部弹出相册、拍照选择框,实现具体功能1:引入插件pubspec.yaml增加image_picker:^0.7.5+22:android添加androidx兼容gradle.properties增加android.useAndroidX=trueandroid.enableJetifier=truebuild添加依赖androidTestImplementation’androidx.tes…

    2022年9月2日
    2
  • 自适应算法应用实例_LMS自适应算法应用实物

    自适应算法应用实例_LMS自适应算法应用实物文章目录一、理论基础1、蝴蝶优化算法2、改进的蝴蝶优化算法(1)柯西变异(2)自适应权重(3)动态切换概率策略(4)算法描述二、函数测试与结果分析三、参考文献四、Matlab仿真程序一、理论基础1、蝴蝶优化算法请参考这里。2、改进的蝴蝶优化算法为了改进蝴蝶算法容易陷入局部最优和收敛精度低的问题,本文从三个方面对蝴蝶算法进行改进。首先通过引入柯西分布函数的方法对全局搜索的蝴蝶位置信息进行变异,提高蝴蝶的全局搜索能力;其次通过引入自适应权重因子来提高蝴蝶的局部搜索能力;最后采用动态切换概率ppp平衡算

    2022年10月6日
    2
  • ubuntu18.04左侧的任务栏dock面板设置「建议收藏」

    ubuntu18.04左侧的任务栏dock面板设置「建议收藏」作用调整任务栏宽度,图标大小,是否隐藏,放置底部1.安装sudoapt-getinstallunity-tweak-toolsudoapt-getinstallgnome-shell-extension-dashtodock2.重启或下面代码alt+f2在窗口里输入r,然后回车等待重启完成3.执行unity-tweak-tool4.设置…

    2022年9月11日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号