ReentrantLock是一个可重入的互斥锁,ReentrantLock由最近成功获取锁,还没有释放的线程所拥有,当锁被另一个线程拥有时,调用lock的线程可以成功获取锁。如果锁已经被当前线程拥有,当前线程会立即返回。此类的构造方法提供一个可选的公平参数
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- }
公平与非公平有何区别,所谓公平就是严格按照FIFO顺序获取锁,非公平安全由程序员自己设计,比如可以按优先级,也可以按运行次数等规则来选择。
在AQS里面有一个state字段,在ReentrantLock中表示锁被持有的次数,它是一个volatile类型的整型值,因此对它的修改可以保证其他线程可以看到。ReentrantLock顾名思义就是锁可以重入,一个线程持有锁,state=1,如果它再次调用lock方法,那么他将继续拥有这把锁,state=2.当前可重入锁要完全释放,调用了多少次lock方法,还得调用等量的unlock方法来完全释放锁。下面简单看一下它的获取与释放过程:
ReentrantLock实现了Lock接口,获取锁是通过lock方法来实现的,整个过程和AQS的获取过程一样,这里不再分析,只分析它的tryAcquire方法的实现。tryAcquire方法有公平版本与非公平版权,源于ReentrantLock使用了两种同步器,具体使用哪一个是在构造方法中提供的公平参数。先看看公平版本的tryAcquire方法:
- /**
- * Fair version of tryAcquire. Don't grant access unless
- * recursive call or no waiters or is first.
- */
- 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;
- }
1、首先判断锁有没有被持有,如果被持有,就判断持有锁的线程是不是当前线程,如果不是就啥也不做,返回获取失败,如果是就增加重入数,返回成功获取;
2、如果锁没有被任何线程持有(c==0),首先判断当前结点前面是否还有线程在排除等待锁,如果有,直接返回获取失败,否则将锁持有数设为acquires,一般为1,然后设置锁的拥有者为当前线程,成功获取。
整个过程比较简单,再来看看非公平的tryAcquire方法:
- /**
- * 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;
- }
代码几乎一模一样,唯一不同的是在知道锁持有数为0时,直接将当前线程设置为锁的持有者,这一点和公平版本的tryAcquire是有区别的,也就是说非公平机制采用的是抢占式模型。
看完了锁的获取,再来看锁的释放,锁的释放就不存在公平与非公平一说。
- 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;
- }
1、首先判断当前线程是不是拥有锁的线程,如果不是,抛出IllegalMonitorStateException异常,这个异常表明表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程;
2、判断释放之后的锁持有数c,如果c!=0,先设置state为该值,然后返回false表示没有被完全释放。如果c==0,表示锁被完全释放,当前线程释放对锁的拥有,返回true,表示已完全释放。
此外ReentrantLock还有一些其他方法,但大部分都是直接代理了AQS中的方法,顺便提一下,可以使用isHeldByCurrentThread() 和 getHoldCount()方法来检查当前线程是否拥有该锁
- final int getHoldCount() {
- return isHeldExclusively() ? getState() : 0;
- }
- public boolean isHeldByCurrentThread() {
- return sync.isHeldExclusively();
- }
- protected final boolean isHeldExclusively() {
- return getExclusiveOwnerThread() == Thread.currentThread();
- }
JDK文档还提到,可以将isHeldExclusively用于调试和测试,例如,只在保持某个锁时才应调用的方法可以声明如下:
- class X {
- ReentrantLock lock = new ReentrantLock();
- // ...
- public void m() {
- assert lock.isHeldByCurrentThread();
- // ... method body
- }
- }
还可以用此方法来确保某个重入锁是否以非重入方式使用的,例如:
- class X {
- ReentrantLock lock = new ReentrantLock();
- // ...
- public void m() {
- assert !lock.isHeldByCurrentThread();
- lock.lock();
- try {
- // ... method body
- } finally {
- lock.unlock();
- }
- }
- }
参考资料:
Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
相关推荐
4、ReentrantLock底层实现和如何使用 5、Condition源码分析 6、ReentrantReadWriteLock底层实现原理 7、并发工具类CountDownLatch 、CyclicBarrier和Semaphore底层实现原理 8、线程池原理和如何使用线程池 9、...
深入java并发编程,使用ReentrantLock和 Synchronized加锁
主要介绍了Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁,本文讲解了ReentrantLock概况、Lock接口、Lock使用、轮询锁的和定时锁、公平性、可中断获锁获取操作等内容,需要的朋友可以参考下
Java并发编程基础主要包括以下几个核心方面: 线程与线程状态:理解Java中线程的基本概念,包括线程的创建、启动、暂停、恢复和终止。熟悉线程的生命周期及其不同状态,如新建、就绪、运行、阻塞和死亡。 线程同步...
java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换? 线程和进程区别 什么是线程和进程? 创建线程有哪几种方式?,如何避免线程死锁 线程的 run()和 start()有什么区别? 什么是 ...
1、本资源包含并发编程基础知识的使用案例,包括:线程创建、Synchronized和Reentrantlock锁的使用、线程安全问题演示、Condition的应用、CountDownLatch的应用、Cyclicbarrier的应用、Semaphore的应用、线程池的...
│ 高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │ 高并发编程第一阶段29讲、如何实现一个自己的显式锁Lock精讲上.mp4 │ 高并发编程第一阶段30讲、如何实现一个自己的显式锁Lock精讲...
java并发编程1-9,可解压,并发编程必看资料。 1 Java 并发编程实践基础 2 构建线程安全应用程序 3 使用JDK 并发包构建程序 4 使用开源软件 Amino 构建并发应用程序 5 数据冲突及诊断工具MTRAT 6 死锁 7 显示锁 ...
Java并发编程实战 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及...
书籍:如《Java并发编程实战》、《Concurrency in C++》等。 官方文档:不同编程语言的官方文档通常会提供关于并发编程的指南和最佳实践。 社区和论坛:如Stack Overflow、Reddit等,可以提供实际问题的帮助和讨论。
【Java并发】当年因为Reentrantlock被拒,今天把它源码里里外外给扒了
Java并发编程学习宝典(漫画版),Java并发编程学习宝典(漫画版)Java并发编程学习宝典(漫画版)Java并发编程学习宝典(漫画版)Java并发编程学习宝典(漫画版)Java并发编程学习宝典(漫画版)Java并发编程学习...
Java多线程并发的程序中使用互斥锁有synchronized和ReentrantLock两种方式,这里我们来详解Java多线程编程中互斥锁ReentrantLock类的用法:
│ 高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │ 高并发编程第一阶段29讲、如何实现一个自己的显式锁Lock精讲上.mp4 │ 高并发编程第一阶段30讲、如何实现一个自己的显式锁Lock精讲...
java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从使用的角度来分析...
主要介绍了java(JUC)ReentrantLock的的相关资料,文中讲解非常详细,实例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
主要为大家详细介绍了Java并发系列之ReentrantLock源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
1、ReentrantLock简介 2、ReentrantLock函数列表 3、重入的实现 4、公平锁与非公平锁 5、ReentrantLock 扩展的功能 6
当然在release()方法中不仅仅只是将state- 1这么简单,- 1之后还需要进行一番处理,如果-1之后的新state = 0,则表示当前锁已经被线程释放
主要为大家详细介绍了Java并发系列之ReentrantLock源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下