首页 > 其他分享 >重入锁ReentrantLock详解

重入锁ReentrantLock详解

时间:2024-09-24 11:24:00浏览次数:11  
标签:重入 Thread synchronized 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高并发程序设计》

标签:重入,Thread,synchronized,ReentrantLock,获取,详解,线程,方法
From: https://blog.csdn.net/qq_38875964/article/details/142469415

相关文章

  • 【ldd命令详解】
    文章目录一、命令概述二、基本语法三、工作原理四、示例五、注意事项......
  • Android面试:OkHttp 详解
    引言        在Android开发中,网络请求是不可或缺的一部分。OkHttp作为一款强大的HTTP客户端库,以其高效、易用和灵活的特点,成为了Android开发者的首选。本文将深入解析OkHttp的内部机制,包括其架构、基本使用、核心组件以及如何通过扩展来实现更丰富的功能。1......
  • Python数据库连接池dbutils详解
    简介在python开发中,如果需要连接MySQL数据库并进行数据操作,可以使用dbutils模块,dbutils是python的一个数据库工具库下载对应模块pipinstallpymysqlpipinstalldbutils连接池配置信息说明使用示例importpymysqlfrompymysql.cursorsimportDictCursorfr......
  • Java 枚举六种常用的方法详解(超详细讲解)
    目录Java枚举  知识点  概念  枚举的方法  枚举的特性  枚举的应用场景  EnumSet和EnumMapJava枚举知识点概念enum的全称为enumeration,是JDK1.5中引入的新特性。在Java中,被enum关键字修饰的类型就是枚举类型。形式如下:enumColor{RED,......
  • struts2配置文件中的method={1}详解
    转载:fifiyong  https://www.cnblogs.com/fifiyong/p/6027565.htmlstruts.xml中的配置:<!--配置用户模块的action--><actionname="user_*"class="userAction"method="{1}"><resultname="registPage">/W......
  • 主从数据库同步配置详解(MySQL/MariaDB)
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、环境准备与安装配置本地部署MySQLUbuntu系统:CentOS系统:MariaDBUbuntu系统:CentOS系统:容器部署MySQLMariaDB二、配置主从库的同步设置四、测试与维护总结前言在数据库管理中,......
  • Stable Diffusion 使用详解(13)--- 3D纹理增强
    目录背景NormalMap描述原理使用心得例子描述原图参数设置底模​编辑正负相关性提示词其他参数controlnet效果还能做点啥调整效果背景实际上,在stablediffusion中,你获取发现很多controlnet其实功能有点类似,你都使用完一遍之后,会发现条条道路通罗马,有的......
  • 文件上传日志包含详解与CTF实战
    1.日志简介1.1日志介绍日志是记录系统或应用程序运行时事件的文件。这些记录可以包括错误信息、用户活动、系统性能指标等,帮助开发者和管理员监控和排查问题。日志通常会记录多种内容,包括:时间戳:事件发生的具体时间。用户代理(UA)头:浏览器或客户端的类型和版本。IP地址:发起......
  • Python中if语句使用详解!
    在Python语言中,if语句是一种条件语句,主要用于根据不同的条件执行不同的操作。接下来,小编通过这篇文章为大家详细讲解一下Python语言if语句,快来学习吧!1、基础语法在Python中,if语句的基本语法是:ifcondition:statement(s)如果条件condition为真,则执行if语句......
  • 滚雪球学SpringCloud[9.2讲]:CI/CD与自动化部署详解
    全文目录:前言1.持续集成与持续交付的基本概念1.1持续集成(CI)1.1.1持续集成的主要优势1.2持续交付(CD)1.2.1持续交付的主要特点1.3CI与CD的区别与联系2.使用Jenkins与GitLab实现CI/CD管道2.1使用Jenkins实现CI/CD2.1.1Jenkins简介2.1.2Jenkins的安装与配置2.1.3......