ReentrantLock的lock(), tryLock(), tryLock(long timeout, TimeUnit unit), lockInterruptibly() 及使用场景示例

ReentrantLock的lock(), tryLock(), tryLock(long timeout, TimeUnit unit), lockInterruptibly() 及使用场景示例一直在用concurrent包里的东西,最近想研究一下个中细节,先从ReentrantLock提供的集中获取锁的方式开始吧。1.ReentrantLock简要介绍简单介绍一下ReentrantLock,可重入锁,互斥锁,提供了fair和unfair两种模式的锁。默认构造函数是unfair的锁,如果初始化时传入true的参数则会返回fair锁。所谓不公平就是在锁可获取时,不用考虑该锁队列是否…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一直在用concurrent包里的东西,最近想研究一下个中细节,先从ReentrantLock提供的集中获取锁的方式开始吧。

1.ReentrantLock简要介绍

简单介绍一下ReentrantLock,可重入锁,互斥锁,提供了fair和unfair两种模式的锁。默认构造函数是unfair的锁,如果初始化时传入true的参数则会返回fair锁。所谓不公平就是在锁可获取时,不用考虑该锁队列是否有其他waiter,直接获取;反之,对于公平锁来讲就是当等待的锁资源可获取时要看下等待队列中当前线程是不是head线程,如果不是则不获取。

简单的看一下获取锁的代码:

//fair lock tryAcquire
if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
//unfair lock nonfairTryAcquire
if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }

从上面的代码块可以清晰的看到,公平锁fairLock在获取之前会看下自己是不是没有前继元素。而非公平锁unfairLock并不会。

2.ReentrantLock提供的获取锁的方式

ReentrantLock提供了lock()、tryLock()、tryLock(long timeout, TimeUnit unit)、lock.lockInterruptibly()

1)lock()

public void lock() {
        sync.lock();
    }
  • 当锁可用,并且当前线程没有持有该锁,直接获取锁并把count set为1.
  • 当锁可用,并且当前线程已经持有该锁,直接获取锁并把count增加1.
  • 当锁不可用,那么当前线程被阻塞,休眠一直到该锁可以获取,然后把持有count设置为1.

小结:该种方式获取锁不可中断,如果获取不到则一直休眠等待。

2)tryLock() 

public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        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;
        }
  • 当获取锁时,只有当该锁资源没有被其他线程持有才可以获取到,并且返回true,同时设置持有count为1;
  • 当获取锁时,当前线程已持有该锁,那么锁可用时,返回true,同时设置持有count加1;
  • 当获取锁时,如果其他线程持有该锁,无可用锁资源,直接返回false,这时候线程不用阻塞等待,可以先去做其他事情;
  • 即使该锁是公平锁fairLock,使用tryLock()的方式获取锁也会是非公平的方式,只要获取锁时该锁可用那么就会直接获取并返回true。这种直接插入的特性在一些特定场景是很有用的。但是如果就是想使用公平的方式的话,可以试一试tryLock(0, TimeUnit.SECONDS),几乎跟公平锁没区别,只是会监测中断事件。

3)tryLock(long timeout, TimeUnit unit)

public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  • 从上面代码中可以看出,获取锁成功或者超时之后返回。而且在公平锁和非公平锁的场景下都可以使用,只是会增加对中断事件的监测。
  • 当获取锁时,锁资源在超时时间之内变为可用,并且在等待时没有被中断,那么当前线程成功获取锁,返回true,同时当前线程持有锁的count设置为1.
  • 当获取锁时,在超时时间之内没有锁资源可用,那么当前线程获取失败,不再继续等待,返回false.
  • 当获取锁时,在超时等待时间之内,被中断了,那么抛出InterruptedException,不再继续等待.
  • 当获取锁时,在超时时间之内锁可用,并且当前线程之前已持有该锁,那么成功获取锁,同时持有count加1.

4)lockInterruptibly()

public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  • 当获取锁时,锁资源可用,那么当前线程成功获得锁,同时持有count设置为1,返回true.
  • 当获取锁时,锁资源可用,当前线程已持有该锁,它成功获取该锁,同时持有count增加1,返回true.
  • 当获取锁时,锁资源不可用,那么该线程开始阻塞休眠等待,但是等待过程中如果有中断事件,那么会停止等待,立即返回.
  • 当获取锁时,锁资源不可用,线程开始阻塞休眠等待,如果等待过程中锁资源变为可用,那么当前线程成功获得锁,同时持有count设置为1,返回true.

小结:lockInterruptibly()获取锁是以排他的模式获取,一旦被中断就放弃等待获取。在等待开始时首先检测中断状态,然后至少调用一次tryAcquire,成功获取就返回true。否则当前线程就开始排队,并且不断的被blocking、unblocking、invoking tryAcquire 直到获取成功或者被中断为止。

3.使用场景示例

下面提供三个demo分别使用lock(), tryLock()的方式获取锁资源,示例展示了持续等待,立即返回,限时等待,中断等场景的使用。

1.如果当前获得锁的线程在做大量耗时的工作,使用lock.lock()方法申请锁的线程会一直阻塞,这样就降低了多线程的效率。而使用tryLock()方法申请锁,如果锁不可用则线程不会阻塞,转而可以去做其他工作。代码实例如下:

public class TestLockAndTryLock {
    private ReentrantLock rlock = new ReentrantLock();

    private void lockTest(){
        long currentTime = System.currentTimeMillis();
        try {
            rlock.lock();

            while (System.currentTimeMillis() - currentTime <= 1000){
                //assume do something
            }
            System.out.println("lockTest----current thread get the lock: " + Thread.currentThread().getName());
        }finally {
            rlock.unlock();
            System.out.println("lockTest----current thread release the lock:  " + Thread.currentThread().getName());
        }
    }

    private void tryLockTest(){

        long currentTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - currentTime <= 100){
            //assume do something
        }

        if (rlock.tryLock()){
            try {
                System.out.println("tryLockTest----current thread get the lock: " + Thread.currentThread().getName());

            }finally {
                rlock.unlock();
                System.out.println("tryLockTest----current thread release the lock: " + Thread.currentThread().getName());
            }

        }else {
            System.out.println("tryLockTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
        }
    }

    public static void main(String[] args){

        TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();

        Thread lockThread = new Thread(
                () -> lockAndTryLock.lockTest(), "Lock-Thread" );

        Thread tryLockThread = new Thread(
                () -> lockAndTryLock.tryLockTest(), "TryLock-Thread" );

        tryLockThread.start();
        lockThread.start();

    }

}
output:

tryLockTest----current thread CAN NOT get the lock: TryLock-Thread
lockTest----current thread get the lock: Lock-Thread
lockTest----current thread release the lock:  Lock-Thread

lock方法不能被中断。如果一个线程在等待获得一个锁时被中断,中断线程在获得锁之前会一直处于 阻塞状态。如果出现死锁,那么lock方法就无法被终止。但是tryLock(long,TimeUnit)在等待超时之后可以结束等待。demo如下:

public class TestLockAndTryLock {
    private ReentrantLock rlock = new ReentrantLock();

    private void lockTest(){
        long currentTime = System.currentTimeMillis();
        try {
            rlock.lock();

            System.out.println("lockTest----current thread get the lock: " + Thread.currentThread().getName());

            while (System.currentTimeMillis() - currentTime <= 5000){
                //assume do something
            }

        }finally {
            rlock.unlock();
            System.out.println("lockTest----current thread release the lock:  " + Thread.currentThread().getName());
        }
    }

    private void tryLockTest(){

        long currentTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - currentTime <= 100){
            //assume do something
        }

        if (rlock.tryLock()){
            try {
                System.out.println("tryLockTest----current thread get the lock: " + Thread.currentThread().getName());


            }finally {
                rlock.unlock();
                System.out.println("tryLockTest----current thread release the lock: " + Thread.currentThread().getName());
            }

        }else {
            System.out.println("tryLockTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
        }
    }


    private void tryLockInterruptTest(){

        long currentTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - currentTime <= 100){
            //assume do something
        }

        try {
            System.out.println("Begin time: " + System.currentTimeMillis());
            if (rlock.tryLock(1, TimeUnit.SECONDS)){
                try {
                    System.out.println("tryLockInterruptTest----current thread get the lock: " + Thread.currentThread().getName());

                }finally {
                    rlock.unlock();
                    System.out.println("tryLockInterruptTest----current thread release the lock: " + Thread.currentThread().getName());
                }

            }else {
                System.out.println("End time: " + System.currentTimeMillis());
                System.out.println("tryLockInterruptTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){

        TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();

        Thread lockThread = new Thread(
                () -> lockAndTryLock.lockTest(), "Lock-Thread" );

        Thread tryLockInterruptThread = new Thread(
                () -> lockAndTryLock.tryLockInterruptTest(), "TryLockInterrupt-Thread"
        );

        tryLockInterruptThread.start();
        lockThread.start();

    }

}
output:
lockTest----current thread get the lock: Lock-Thread
Begin time: 1533636472680
End time: 1533636473681
tryLockInterruptTest----current thread CAN NOT get the lock: TryLockInterrupt-Thread
lockTest----current thread release the lock:  Lock-Thread

同时,tryLock(long, TimeUnit)可以被中断,demo如下:

private void tryLockInterruptTest(){

        long currentTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - currentTime <= 100){
            //assume do something
        }

        try {
            System.out.println("Begin time: " + System.currentTimeMillis());
            if (rlock.tryLock(3, TimeUnit.SECONDS)){
                try {
                    System.out.println("tryLockInterruptTest----current thread get the lock: " + Thread.currentThread().getName());

                }finally {
                    rlock.unlock();
                    System.out.println("tryLockInterruptTest----current thread release the lock: " + Thread.currentThread().getName());
                }

            }else {
                System.out.println("End time: " + System.currentTimeMillis());
                System.out.println("tryLockInterruptTest----current thread CAN NOT get the lock: " + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            System.out.println("tryLockInterruptTest Interrupt----current thread is interrupted: " + Thread.currentThread().getName());
        }
    }

    public static void main(String[] args){

        TestLockAndTryLock lockAndTryLock = new TestLockAndTryLock();

        Thread lockThread = new Thread(
                () -> lockAndTryLock.lockTest(), "Lock-Thread" );

        Thread tryLockInterruptThread = new Thread(
                () -> lockAndTryLock.tryLockInterruptTest(), "TryLockInterrupt-Thread"
        );

        tryLockInterruptThread.start();
        lockThread.start();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "is interrupted now. ");
        }

        tryLockInterruptThread.interrupt();

    }
output:

lockTest----current thread get the lock: Lock-Thread
Begin time: 1533637530378
tryLockInterruptTest Interrupt----current thread is interrupted: TryLockInterrupt-Thread
lockTest----current thread release the lock:  Lock-Thread

很明显被中断了,没有完成等待。

以上是对ReentrantLock的几种获取锁的方法的详解,并附以demo示例,如有疑问可以留言讨论。

 

refer:https://blog.csdn.net/u011784767/article/details/51659701

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

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

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

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

(0)


相关推荐

  • DM368开发 — 文件烧写[通俗易懂]

    DM368开发 — 文件烧写[通俗易懂]参看:DM36x的UBL分析以及串口启动UBL是RBL引导启动的一段小程序,主要负责初始化时钟,串口,NAND,DDR2等,然后把uboot,kernel,rootfs复制到DDR2上并引导uboot。为什么UBL跟串口启动一起讲,那是因为这两个关系很密切,很多代码是共用的,而且代码都放在同一个目录下,所以就合起来一起讲了。一、UBLubl的代码放在dvsdk目录下

  • django 视图_基本视图有哪些

    django 视图_基本视图有哪些视图家族drf的视图总共分为以下4个,对应4个源码文件views:视图类generics:工具视图mixins:视图工具集viewsets:视图集学习曲线我们学习视图,可以按照以下的曲线

  • hive 正则表达式详解[通俗易懂]

    hive 正则表达式详解[通俗易懂]hive中的正则表达式还是很强大的。数据工作者平时也离不开正则表达式。对此,特意做了个hive正则表达式的小结。所有代码都经过亲测,正常运行。1.regexp语法:AREGEXPB操作类型:strings描述:功能与RLIKE相同selectcount(*)fromolap_b_dw_hotelorder_fwherecreate_date_widnotregexp

  • vue组件化的理解_什么是前端组件化

    vue组件化的理解_什么是前端组件化前言有时候有一组html结构的代码,并且这个上面可能还绑定了事件。然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的。那么这时候我们就可以

  • mysql数据库select语句用法_mysql数据库select查询语句简单用法「建议收藏」

    mysql数据库select语句用法_mysql数据库select查询语句简单用法「建议收藏」mysqlselect简单用法1、select语句可以用回车分隔$sql=”select*fromarticlewhereid=1″和$sql=”select*fromarticlewhereid=1″,都可以得到正确的结果,但有时分开写或许能更明了一点,特别是当sql语句比较长时2、批量查询数据可以用in来实现$sql=”select*fromarticlewher…

  • 安全 | 几种wifi密码破解的思路

    从一个攻击者的视角,多种方案,讲述破解一台路由器进而控制对方网络的过程,以此提醒广大用户,提高安全意识,做好安全措施。

发表回复

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

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