ReentrantLock 使用

ReentrantLock 介绍

ReentrantLock 是 Java 中的一个可重入锁,它提供了与 synchronized 关键字类似的功能,但相比 synchronized,ReentrantLock 提供了更多的灵活性和功能。

定义:ReentrantLock 是一个可重入且独占式的锁,它具有与使用 synchronized 监视器锁相同的基本行为和语义,但与 synchronized 关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。

ReentrantLock,顾名思义,它是支持可重入锁的锁,是一种递归无阻塞的同步机制。除此之外,该锁还支持获取锁时的公平和非公平选择。

公平性:ReentrantLock 的内部类 Sync 继承了 AQS,分为公平锁 FairSync 和非公平锁 NonfairSync。

如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平锁的获取,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。

ReentrantLock 的公平与否,可以通过它的构造函数来决定。

ReentrantLock 的主要特点包括:

  1. 可重入性:允许线程多次获取同一把锁,而不会造成死锁。
  2. 公平性:可以选择是否公平地获取锁,公平性可以保证等待时间最长的线程优先获取锁。
  3. 中断响应:支持线程在等待锁的过程中响应中断。
  4. 条件变量:可以使用条件变量来实现更复杂的线程通信。
  5. 锁超时:可以设置获取锁的超时时间。

ReentrantLock 的主要方法包括:

  1. lock():获取锁,如果锁已经被其他线程获取,则当前线程会被阻塞,直到获取到锁。
  2. unlock():释放锁。
  3. tryLock():尝试获取锁,如果获取成功返回 true,否则返回 false。
  4. newCondition():创建一个 Condition 对象,用于线程等待和通知。
复制代码
@Slf4j
public class LockExample2 {

    // 请求总数
    public static int clientTotal = 5000;
    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static int count = 0;
    private final static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}
复制代码

 

tryLock 方法

我们之前进行过介绍,Lock 接口包含了两种 tryLock 方法,一种无参数,一种带参数。

  • boolean tryLock():仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false;
  • boolean tryLock(long time, TimeUnit unit):如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁;
复制代码
 try {
            if(locks.tryLock(4000,TimeUnit.MILLISECONDS)){ //尝试获取锁,获取成功则进入执行,不成功则执行finally模块
                System.out.println(Thread.currentThread().getName()+"-->");
                Thread.sleep(5000);
            }else{
                System.out.println(Thread.currentThread().getName()+" time out ");
            }
        } catch (InterruptedException e) {
             e.printStackTrace();
        }finally {
            try {
                locks.unlock();
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + "未获取到锁,释放锁抛出异常");
            }
        }
复制代码

结果分析:tryLock 方法,虽然等待 4000 毫秒,但是这段时间不足以等待 Thread-1 释放资源锁,所以还是超时。

公平锁与非公平锁

分类:根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁。

公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。

非公平锁:非公平锁则在运行时闯入,不遵循先到先执行的规则。

ReentrantLock:ReentrantLock 提供了公平和非公平锁的实现。

ReentrantLock 实例:

//公平锁
ReentrantLock pairLock = new ReentrantLock(true);
//非公平锁
ReentrantLock pairLock1 = new ReentrantLock(false);
//如果构造函数不传递参数,则默认是非公平锁。
ReentrantLock pairLock2 = new ReentrantLock();

场景介绍:通过模拟一个场景假设,来了解公平锁与非公平锁。

  • 假设线程 A 已经持有了锁,这时候线程 B 请求该锁将会被挂起;
  • 当线程 A 释放锁后,假如当前有线程 C 也需要获取该锁,如果采用非公平锁方式,则根据线程调度策略,线程 B 和线程 C 两者之一可能获取锁,这时候不需要任何其他干涉;
  • 而如果使用公平锁则需要把 C 挂起,让 B 获取当前锁,因为 B 先到所以先执行。

Tips:在没有公平性需求的前提下尽量使用非公平锁,因为公平锁会带来性能开销。

 ReentrantLock 其他方法介绍

对 ReentrantLock 来说,方法很多样,如下介绍 ReentrantLock 其他的方法,有兴趣的同学可以自行的尝试使用。

  • getHoldCount():当前线程调用 lock () 方法的次数;
  • getQueueLength():当前正在等待获取 Lock 锁的线程的估计数;
  • getWaitQueueLength(Condition condition):当前正在等待状态的线程的估计数,需要传入 Condition 对象;
  • hasWaiters(Condition condition):查询是否有线程正在等待与 Lock 锁有关的 Condition 条件;
  • hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取 Lock 锁;
  • hasQueuedThreads():查询是否有线程正在等待获取此锁定;
  • isFair():判断当前 Lock 锁是不是公平锁;
  • isHeldByCurrentThread():查询当前线程是否保持此锁定;
  • isLocked():查询此锁定是否由任意线程保持。

 

 

 

posted @   黄橙  阅读(261)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示