ReentrantLock 介绍
ReentrantLock 是 Java 中的一个可重入锁,它提供了与 synchronized 关键字类似的功能,但相比 synchronized,ReentrantLock 提供了更多的灵活性和功能。
定义:ReentrantLock 是一个可重入且独占式的锁,它具有与使用 synchronized 监视器锁相同的基本行为和语义,但与 synchronized 关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。
ReentrantLock,顾名思义,它是支持可重入锁的锁,是一种递归无阻塞的同步机制。除此之外,该锁还支持获取锁时的公平和非公平选择。
公平性:ReentrantLock 的内部类 Sync 继承了 AQS,分为公平锁 FairSync 和非公平锁 NonfairSync。
如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平锁的获取,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。
ReentrantLock 的公平与否,可以通过它的构造函数来决定。
ReentrantLock 的主要特点包括:
- 可重入性:允许线程多次获取同一把锁,而不会造成死锁。
- 公平性:可以选择是否公平地获取锁,公平性可以保证等待时间最长的线程优先获取锁。
- 中断响应:支持线程在等待锁的过程中响应中断。
- 条件变量:可以使用条件变量来实现更复杂的线程通信。
- 锁超时:可以设置获取锁的超时时间。
ReentrantLock 的主要方法包括:
- lock():获取锁,如果锁已经被其他线程获取,则当前线程会被阻塞,直到获取到锁。
- unlock():释放锁。
- tryLock():尝试获取锁,如果获取成功返回 true,否则返回 false。
- 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():查询此锁定是否由任意线程保持。
标签:Thread,ReentrantLock,获取,线程,tryLock,使用,公平 From: https://www.cnblogs.com/RedOrange/p/18056899