JAVA并发编程ReentrantLock核心原理剖析

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文介绍了Java并发编程中ReentrantLock的重要性和优势,详细解析了其原理及源码实现。ReentrantLock作为一种可重入锁,弥补了synchronized的不足,如支持公平锁与非公平锁、响应中断等。文章通过源码分析,展示了ReentrantLock如何基于AQS实现公平锁和非公平锁,并解释了两者的具体实现过程。

JAVA并发编程系列以及陆续出了5篇,第六篇的主角ReentrantLock该出场了。之前《一文看懂全部锁机制》谈到可重入锁、《JAVA并发编程AQS原理剖析》谈到了JUC灵魂AQS,那么AQS的思想优秀实践者ReentrantLock是怎么实现AQS的呢?

1、ReentrantLock是什么,有哪些优点

    ReentrantLock英文翻译以及顾名思义:可重入锁。之前文章说过,还有synchronized也是可重入锁。竟然JDK最开始有了synchronized这个可重入锁,而且JDK1.6之后也对synchronized进行锁优化,性能堪比JUC里的其他Lock()。为什么还提供了ReentrantLock呢?在《JAVA并发编程volatile核心原理》文中开头我们就简单的列了synchronized的几个缺点,包括:阻塞时间过长,不可中断、是非公平锁。

所以,同样是可重入锁,ReentrantLock必须有一些亮点。它的优点就是较好的补足synchronized的缺点,提供了非常灵活多变的特性,满足系统项目研发的需求。


     优点有:

     1、支持公平锁+非公平锁。(synchronized仅支持非公平锁)

     2、支持响应中断、超时。(synchronized不支持超时)

     3、支持线程尝试获取锁。(synchronized不支持)


2、具体说说ReentrantLock的原理,看过ReentrantLock源码吗


    话说到这份上,好像不看源码不行了。上源码,不要慌,它的核心源码就这几行,先扫一眼:


刚面试官问:具体说说它的原理?


少废话,一句话总结它的原理:ReentrantLock是AQS的具体实现,实现了公平锁和不公平锁。ReentrantLock源码里有三个内部类。


一个是Sync抽象类;sync 继承了AQS 队列同步器。所以ReentrantLock是AQS的实现。

一个是FailSync类,实现了Sync类,实现的是公平锁。

一个是NonFairSync类,也是实现Sync类,实现的是非公平锁。


“好家伙,总结的还不错”面试官心嘀咕着,继续问:


2.1 公平锁是怎么实现的?

    我们直接看源码,公平锁的lock加锁是acquire(1)方法。

接下来源码是,就看到这里就很清晰了。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }


首先,公平锁加锁可能有三部逻辑执行。

第一步:tryAcquire(1),尝试去获取锁。如果成功了,就没有后面两个步骤的事。

第二步:如果第一步获取失败,把当前线程封装为独占锁类型NODE 进入AQS 的FIFO 队列,等待被按顺序唤醒。

第三步:第二步执行后,也会调用selfInterrupt(),里面的源码就是线程自我中断,修改中断标识位为:true。



那具体再说说第一步tryAcquire(1),是如何获取线程锁的?(候选人内心有点破防:面试官问题咋那么多...)


具体就是:

1、先判断当前AQS队列的state是否为0,如果是0,说明现在可以竞争锁。

2、接下来继续判断AQS的同步队列,如果队列是空、或者队列不为空且队列里的节点是不是自己,就去竞争锁。

3、如果竞争锁成功,通过CAS去设置AQS的state值为1,并帮AQS的当前占有锁的NODE 引用线程改完自己。


源码如下:

2.2 非公平锁又是怎么实现的?

   new ReentrantLock()默认的就是非公平锁。

public ReentrantLock() {
        sync = new NonfairSync();
    }


非公平锁的竞争锁就比较暴力,上来就直接通过CAS去竞争,

如果获取失败,进入acquire(1),继续去尝试获取锁。

这里和公平锁也是不一样,它没有判断队列情况,继续用CAS方式去看能否修改state获取锁。


如果竞争失败,就会进入AQS队列,等待被唤醒....

今天暂时分享到这,其实可重入锁里面的源码还有很多内容,篇幅有限,等源码专区出来再全部分享。

相关文章
|
1月前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
10天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
14天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
49 12
|
11天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
26 3
|
11天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
43 2
|
11天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
92 2
|
27天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
28天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
50 3
|
Java Android开发
【Java 虚拟机原理】Java 引用类型 ( 强引用 | 软引用 | 弱引用 | 虚引用 | 静态变量 )
【Java 虚拟机原理】Java 引用类型 ( 强引用 | 软引用 | 弱引用 | 虚引用 | 静态变量 )
159 0
|
8天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者