当前位置: 首页 > news >正文

重入锁ReentrantLock详解

目录

ReentrantLock简介

可重入性

ReentrantLock的特性

中断响应

公平锁与非公平锁

非阻塞获取锁

Condition类

与synchronized的比较

总结

参考

ReentrantLock简介

重入锁ReentrantLock是Java并发包中提供的一种可重入锁,它相较于Java的synchronized关键字具有更多的功能和更细粒度的控制。以下是关于ReentrantLock的详细介绍:

可重入性

可重入性是指同一个线程在没有释放锁的情况下,可以多次获得同一个锁。可重入性对于递归调用非常有用,因为它允许线程在持有锁的情况下再次获取该锁,而不会导致死锁。synchronized和ReentrantLock都是可重入的。

ReentrantLock的特性

中断响应

使用关键字synchronized若产生了死锁,那么陷入死锁的线程将一直等待,而ReentrantLock提供了一种中断响应机制,使用ReentrantLock的lockInterruptibly()方法获取锁线程可以响应中断,如果获取锁时,或者持有锁线程陷入阻塞等待,若此时线程被中断,将会抛出InterruptedException异常,中断机制对处理死锁有一定帮助,也为并发编程提供了更多的灵活性,请看下面示例:

import java.util.concurrent.locks.ReentrantLock;public class RLock {public static void main(String[] args) {ReentrantLock lock1 = new ReentrantLock();ReentrantLock lock2 = new ReentrantLock();Thread thread1 = new Thread(() -> {try {lock1.lockInterruptibly();System.out.println("线程1获取到锁1");Thread.sleep(2000);lock2.lockInterruptibly();System.out.println("线程1获取到锁2,线程1任务完成");} catch (InterruptedException e) {System.out.println("线程1被中断,放弃任务");} finally {//判断当前线程是否保持此锁定if (lock1.isHeldByCurrentThread()) lock1.unlock();if (lock2.isHeldByCurrentThread())lock2.unlock();}});Thread thread2 = new Thread(() -> {try {lock2.lockInterruptibly();System.out.println("线程2获取到锁2");Thread.sleep(2000);lock1.lockInterruptibly();System.out.println("线程2获取到锁1,线程2任务完成");} catch (InterruptedException e) {System.out.println("线程2被中断,放弃任务");} finally {if (lock1.isHeldByCurrentThread()) lock1.unlock();if (lock2.isHeldByCurrentThread())lock2.unlock();}});thread1.start();thread2.start();try {Thread.sleep(3000);thread1.interrupt();} catch (InterruptedException e) {e.printStackTrace();}}
}
//输出
线程1获取到锁1
线程2获取到锁2
线程1被中断,放弃任务
线程2获取到锁1,线程2任务完成

上面示例代码中,利用两个线程交替获取两个锁产生了死锁,若没有其它措施,这两个线程将一直保持阻塞状态,得益于中断响应机制,我们在3秒后向线程1发送中断请求,线程1退出阻塞状态并释放锁,线程2就可以获取到全部锁完成任务,而线程1放弃了任务,释放资源,通过中断机制避免了死锁。

公平锁与非公平锁
  • 公平锁指的是锁的分配机制是公平的,通常先对锁提出获取请求的线程会先被分配到锁。

  • 非公平锁是允许插队的锁,即后来请求的线程可能先获得锁。非公平锁可以减少线程切换和上下文切换的开销,因此性能通常比公平锁高。

synchronized是非公平锁,ReentrantLock默认情况下也是非公平锁,但是在构造函数中提供了公平锁的初始化方式。

非阻塞获取锁

ReentrantLock提供了tryLock()方法,该方法尝试获取锁,如果锁被其他线程占用,则立即返回false,而不是阻塞当前线程。这可以用于实现一些非阻塞的并发控制逻辑。tryLock()方法还能设置超时时间,即尝试等待一段时间,如果在这段时间内锁没有被获取到,则线程将不再等待,直接返回false。

Condition类

如果你知道wait()方法和notify()方法,那么理解Condition就很容易了。它与wait()方法和notify()方法的作用是大致相同的。但是wait()方法和notify()方法是与synchronized关键字合作使用的,而Condition是与ReentrantLock配合使用的。其主要方法如下:

  • await()方法会使当前线程等待,同时释放当前锁,与wait()方法相似。

  • awaitUninterruptibly()方法与await()方法基本相同,但是它不响应中断。

  • singal()方法用于唤醒一个在等待中的线程,与notify()方法类似,singalAll()方法与notifyAll()方法类似。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class RLock {public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();Condition condition = lock.newCondition();Thread thread = new Thread(() -> {try {lock.lock();condition.await();System.out.println("线程执行完成");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}});thread.start();Thread.sleep(3000);lock.lock();condition.signal();lock.unlock();}
}

上面例子中启动了一个子线程,调用await()方法让子线程陷入阻塞,主线程在3秒钟后通过signal()方法将其唤醒,子线程得以从阻塞状态退出,完成接下来的任务。需要注意的是执行await()方法和signal()方法都需要获取锁,这点也跟wait()方法和notify()方法一样。

与synchronized的比较

与synchronized相比,ReentrantLock提供了更多的功能和灵活性,例如支持公平锁、可中断的锁获取、尝试非阻塞地获取锁等,ReentrantLock可以实现更细粒度的锁控制和更复杂的同步控制逻辑。在性能方面在Java6之前synchronized是重量级锁,其性能会比较差,Java6引入了锁升级策略,提高了synchronized的并发性能,与ReentrantLock的性能相差不大。使用方面synchronized更简单方便,ReentrantLock需要显示地获取锁和释放锁,且获取锁和释放锁的次数要相等,所以一般要在finally块中释放锁,以确保锁被正确释放。

总结

ReentrantLock是Java并发包中提供的一种功能强大、使用灵活的可重入锁。它支持公平锁和非公平锁、显式锁控制、尝试非阻塞地获取锁、支持超时尝试获取锁、支持中断以及条件变量等特性。在需要更细粒度的锁控制或避免synchronized关键字的限制时,ReentrantLock是一个很好的选择。然而,在使用时需要注意确保在finally块中释放锁,以避免死锁的发生。

参考

《Java高并发程序设计》


http://www.mrgr.cn/news/34828.html

相关文章:

  • axios提交数据后台php用post方式无法正确接收
  • Oracle AI理论与实践,企业落地篇干货满满
  • 使用Python实现深度学习模型:智能电影制作与剪辑
  • FastAPI 第二课 -- 安装
  • 请不要自己写,Spring Boot非常实用的内置功能
  • 测试文件只能
  • python爬虫初体验(二)
  • 9.23作业
  • 网络工程/售后类面试题
  • CentOS一键安装Mosquitto开源消息代理结合内网穿透实现远程连接
  • 浅谈C++之多线程实现
  • 软考高级:企业信息化-数据挖掘中的上卷和下钻、旋转分析
  • tortoies-orm 一对一、一对多和多对多关系实现
  • 设计模式之中介者
  • k8s知识汇编
  • 前端导出excel表格
  • 分布式难题-三座大山NPC
  • 【JAVA-数据结构】初识集合框架
  • vue2和vue3页面加自定义水印(组件化)
  • 828华为云征文 | 华为云Flexusx与OwnCloud的完美融合,打造高效云端办公环境