究竟什么是可重入锁?

究竟什么是可重入锁?经历很久之前就听说了可重入锁,可重入锁究竟是什么意思,以前是囫囵吞枣的,只要记住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)
上一篇 2022年6月26日 上午11:16
下一篇 2022年6月26日 上午11:16


相关推荐

  • CSP考试总结_考试总结300字

    CSP考试总结_考试总结300字t1模拟很难受,要加大这方面练习,要习惯用草稿理清思路t2和去年一样的东西,也是高精的坑,考前应该补一下高精t3比较正常,这种大数据结构只有考虑暴力,打正解很容易浪费时间并且上头t4无能

    2022年8月6日
    6
  • react子组件向父组件传递数据_react子组件改变父组件的状态

    react子组件向父组件传递数据_react子组件改变父组件的状态本博客代码是React父组件和子组件相互传值的demo;实现封装一个折线图,折线图选择下拉框,获取下拉框点击的值并且传给父组件根据下拉框筛选的条件更新视图;效果图如下:父组件代码:代码解析:父组件Parent引用子组件Sub,传递了list组件给子组件,并且接收子组件传递给父组件的storeId;importReact,{Component}fro…

    2025年9月12日
    6
  • mysql查看有哪些函数_MySQL函数一览_MySQL函数全部汇总

    mysql查看有哪些函数_MySQL函数一览_MySQL函数全部汇总前言 MySQL 提供了众多功能强大 方便易用的函数 使用这些函数 可以极大地提高用户对于数据库的管理效率 从而更加灵活地满足不同用户的需求 本文将 MySQL 的函数分类并汇总 以便以后用到的时候可以随时查看 数学函数 1 ABS x 返回 x 的绝对值 2 PI 返回圆周率 默认显示 6 位小数 3 SQRT x 返回非负数的 x 的二次方根 4 MOD x y 返回 x 被 y 除后的余数 5 CEIL x CE

    2026年3月16日
    1
  • Linux系统调用Hook姿势总结

    Linux系统调用Hook姿势总结http://www.cnblogs.com/LittleHann/p/3854977.html主题Linux相关学习资料http://xiaonieblog.com/?post=121http://hbprotoss.github.io/posts/li-yong-ld_preloadjin-xing-hook.htmlhttp://www.catonma

    2022年5月9日
    228
  • java random.nextint_java Random.nextInt()方法的具体使用

    java random.nextint_java Random.nextInt()方法的具体使用licintnextInt(intn)该方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。直接上代码:packageorg.xiaowu.random.demo;importjava.util.Random;importorg.junit.Test;publicclassRandomDemo{@Testpublicv…

    2022年7月22日
    13
  • Python读写LMDB文件「建议收藏」

    Python读写LMDB文件「建议收藏」LMDB的全称是LightningMemory-MappedDatabase,它的文件结构简单,包含一个数据文件和一个锁文件。LMDB文件可以同时由多个进程打开,具有极高的数据存取速度,访问简单,不需要运行单独的数据库管理进程,只要在访问数据的代码里引用LMDB库,访问时给文件路径即可。让系统访问大量小文件的开销很大,而LMDB使用内存映射的方式访问文件,使得文件内寻址的开销非常小,使…

    2026年4月18日
    8

发表回复

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

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