• Java 显示锁 之 重入锁 ReentrantLock(七)


    ReentrantLock 重入锁简介

    重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁。另外,该锁还支持获取锁时的公平与非公平性的选择。

    重入锁 ReentrantLock,只支持独占方式的获取操作,因此它只实现了 tryAcquire、tryRelease 和 isHeldExclusively 方法。

    ReentrantLock 如何实现锁重入

    锁重入是指任意线程在获取到锁之后,能够再次获取该锁而不会被锁所阻塞,该特性的实现需要解决两个问题:

    1、线程再次获取锁

    需要识别获取锁的线程是否为当期占有锁的线程,如果是,则便获取锁成功

    2、锁最终得到释放

    同一个线程重复 n 次获得了锁,在第 n 次释放该锁后,其它线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时,表示锁已经成功释放


    ReentrantLock 是通过自定义同步器来实现所得获取和释放,默认使用非公平性,下面就以非公平性获取锁为例,学习重入锁是如何实现的:

     final boolean nonfairTryAcquire(int acquires) {
                //当前线程
                final Thread current = Thread.currentThread();
                //当前同步状态
                int c = getState();
                //当前同步状态等于0,说明当前线程可以获取同步状态
                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;
            }

    通过判断当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程,则将同步状态值进行累加并返回true,表示获取同步状态成功。

    成功获取锁的线程再次获取锁,只是增加了同步状态值,这也就要求在释放同步状态时递减同步状态的值。如下,释放锁的代码:

    protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }

    如果该锁被获取了n次, 那么前( n- 1) 次 tryRelease( int releases) 方法必须返回false,只有同步状态完全释放了,才能返回tru。 该方法将同步状态 是否为 0 作为作为最终释放的条件,当同步状态为0时,将该锁的拥有者线程设置为null,并返回true,表示成功释放。

    ReentrantLock 如何实现锁的公平性与非公平性

    公平性与否是针对获取锁而言的,如果一个锁时公平的,那么锁的获取顺序就应该服务请求时间的顺序,也就是FIFO。

    由上文介绍的非公平获取锁方法 nonfairTryAcquire( int acquires) 可知,只要CAS设置同步状态成功,则表示线程获取了锁,而公平锁则不同,如代码所示:

    static final class FairSync extends Sync {
    
    protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    }

    该方法与 nonfairTryAcquire( int acquires) 的区别在于多了hasQueuedPredecessors() 方法,即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,则表示有线程比当前线程更早的请求了锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。

  • 相关阅读:
    引用同一解决方案里的其他项目
    使用OpenXml打开word文档中嵌入的另一个文档
    把最近踩的坑总结一下(2)——二级MSoffice题库软件自动更新版本V2.0
    把最近踩的坑总结一下(1)——二级MSoffice题库软件自动更新版本V2.0
    脑残系列(1)
    前端Crypto.JS加密解密
    在构造函数时 new做的四件事情
    js创建对象的四种方式
    VS-Code背景图片
    单行、多行文本溢出以省略号显示
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052595.html
Copyright © 2020-2023  润新知