究竟什么是可重入锁?

究竟什么是可重入锁?经历很久之前就听说了可重入锁,可重入锁究竟是什么意思,以前是囫囵吞枣的,只要记住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来计数重入次数,避免了频繁的持有释放操作,这样既提升了效率,又避免了死锁。

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Sql语句里的递归查询(转)

    Sql语句里的递归查询(转)

  • 提高 Activity 跳转速度

    提高 Activity 跳转速度从一个页面跳转到另外一个页面,这在Android中是很常见的操作;有的页面跳转是瞬间打开,有的是会微微钝一下才打开;或者在页面里弹出一个Dialog弹框,明明已经调用完了show()方法,但弹框没马上弹出来,延迟了一百多毫秒才显示出来,这样给人的体验就很不好了。为什么会出现这种情况呢?页面跳转迟钝和弹框的展示延迟的原因都一样,基本分为两大类。第一类就是看看是否在UI线程左耗时操作,这个经常是数据方面的问题,比如说要从数据库里面查询数据,或者说对数据做了一些复杂逻辑的操作,例如要对集合中的数据,

  • Java四舍五入保留小数点后几位

    Java四舍五入保留小数点后几位(double)+Math.round返回double类型//案例:四舍五入保留小数点后1位doubled1=123.456;doubled2=654.321;doublev1=(double)Math.round(d1*10)/10;doublev2=(double)Math.round(d2*10)/10;System.out…

  • 设置Textview最大长度,超出显示省略号

    其中很关键推荐:http://www.cnblogs.com/roucheng/p/androidwujie.html

    2021年12月27日
  • c6000 中断小结

    c6000 中断小结1. 中断相关概念中断过程———————————–你在下象棋,突然电话响了,你回屋接电话,然后回来继续下象棋,这个过程就叫做中断响应过程。CPU执行正常任务———————下象棋保护现场———————————-你已经想好要“将军”,先在脑海中记下来。中断

  • LINUX系统更改系统时区

    LINUX系统更改系统时区Linux-Redhat系统更改系统时区[root@localhost~]#date #查看本地时间ThuMay1923:41:32EDT2022[root@localhost~]#hwclock–show #查看硬件时间Thu19May202211:42:07PMEDT-0.332325seconds当前系统版本:RedHatEnterpriseLinuxServerrelease7.0(Maipo)查看目前的时区:[r

    2022年10月30日

发表回复

您的电子邮箱地址不会被公开。

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