Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法

Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法 - 程序员古德

Java11中的Lock接口提供lock()lockInterruptibly()两种锁定方法,用于获取锁,但处理线程中断时有所不同,lock()使线程等待直到锁释放,期间无视中断;而lockInterruptibly()在等待中若收到中断请求,会立即响应并抛出异常,这种机制使lockInterruptibly()更灵活,能更好处理线程中断,避免无意义等待,提升系统响应性和效率。

定义

Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法 - 程序员古德

在Java 11中,Lock接口提供了两种锁定方法:lock()lockInterruptibly(),这两种方法虽然都是为了获取锁,但在处理线程中断时有着显著的不同。

lock()方法是一种基本的锁定机制,当一个线程调用这个方法时,如果锁已经被其他线程持有,那么调用线程就会进入等待状态,直到锁被释放,在等待的过程中,这个线程会无视中断请求,也就是说,即使有其他线程调用了这个等待线程的interrupt()方法,它也不会有任何响应,依旧会“执着”地等待锁的释放。

lockInterruptibly()方法则不同,它提供了一种更加“灵活”的锁定机制,当一个线程调用这个方法时,如果锁已经被其他线程持有,那么这个线程同样会进入等待状态,但是,在等待的过程中,如果这个线程收到了中断请求(即有其他线程调用了它的interrupt()方法),那么它就会立即响应中断,不再等待锁的释放,而是抛出一个InterruptedException异常。

举个例子来说明这两种方法的区别,假设有一个仓库,只有一把钥匙可以打开(这里把钥匙比作锁),现在有两个人(线程)都想进入仓库取东西,

  1. 如果第一个人使用lock()方法,那么在他拿到钥匙并进入仓库之前,即使有人叫他(发送中断请求),他也不会理睬,直到他取完东西出来并把钥匙交给下一个人。
  2. 如果第一个人使用lockInterruptibly()方法,那么在他等待钥匙的时候,如果有人叫他(发送中断请求),他就会立即响应,放弃等待,转而去做其他事情。

lockInterruptibly()方法就提供了一种更加“人性化”的锁定机制,能够更好地处理线程的中断请求,避免线程长时间无意义地等待锁的释放。

代码案例

Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法 - 程序员古德

使用lock()

Lock接口通过其实现类ReentrantLock来使用,以下是一个使用ReentrantLocklock()方法的示例代码,该代码模拟了上面描述的“仓库”案例,在这个例子中,有一个Warehouse类,它包含一个资源,并且使用ReentrantLock来控制对这个资源的访问,如下代码:

import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  // 仓库类,使用Lock来控制访问  
public class Warehouse {  private final Lock lock = new ReentrantLock();  private String resource = "Goods inside the warehouse"; // 仓库中的资源  // 获取仓库资源的方法  public void accessResource() {  lock.lock(); // 获取锁  try {  // 模拟长时间的操作  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  // 注意:这里不会响应中断,因为使用了lock()方法  e.printStackTrace();  }  System.out.println(Thread.currentThread().getName() + " accessed the resource: " + resource);  } finally {  lock.unlock(); // 释放锁  }  }  
}  // 客户端代码,模拟两个线程同时尝试访问仓库资源  
public class Client {  public static void main(String[] args) {  Warehouse warehouse = new Warehouse();  // 创建并启动两个线程,它们将尝试访问仓库资源  Thread thread1 = new Thread(() -> warehouse.accessResource(), "Thread-1");  Thread thread2 = new Thread(() -> warehouse.accessResource(), "Thread-2");  thread1.start();  thread2.start();  // 注意:由于使用了lock()方法,即使尝试中断线程,它们也不会响应,直到它们自然地完成操作  }  
}

在上面代码中,Warehouse类有一个accessResource方法,它使用lock()方法获取锁,并在finally块中释放锁,以确保锁总是会被释放,无论是否发生异常,Client类创建了两个线程,它们几乎同时尝试访问Warehouse的资源,由于lock()方法不会响应中断,因此即使一个线程在持有锁时被中断,它也会继续执行直到释放锁。

这个代码输出内容如下,因为ReentrantLock确保了资源在任何时候只被一个线程访问,如下:

Thread-1 accessed the resource: Goods inside the warehouse  
Thread-2 accessed the resource: Goods inside the warehouse

使用lockInterruptibly()

下面使用Lock接口中的lockInterruptibly()方法的Java代码示例,演示如何中断一个正在等待获取锁的线程,如下代码:

import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  // 仓库类,使用Lock来控制访问  
public class Warehouse {  private final Lock lock = new ReentrantLock();  private String resource = "Goods inside the warehouse"; // 仓库中的资源  // 可响应中断地获取仓库资源的方法  public void accessResourceInterruptibly() throws InterruptedException {  lock.lockInterruptibly(); // 可响应中断地获取锁  try {  // 模拟长时间的操作  Thread.sleep(2000);  System.out.println(Thread.currentThread().getName() + " accessed the resource: " + resource);  } finally {  lock.unlock(); // 无论如何都会释放锁  }  }  
}  // 客户端代码,模拟两个线程同时尝试访问仓库资源,并中断其中一个线程  
public class Client {  public static void main(String[] args) throws InterruptedException {  Warehouse warehouse = new Warehouse();  // 创建第一个线程并启动,它将尝试访问仓库资源  Thread thread1 = new Thread(() -> {  try {  warehouse.accessResourceInterruptibly();  } catch (InterruptedException e) {  System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");  }  }, "Thread-1");  thread1.start();  // 让第一个线程有时间去获取锁  Thread.sleep(500);  // 创建第二个线程,它也将尝试访问仓库资源,但会中断它  Thread thread2 = new Thread(() -> {  try {  warehouse.accessResourceInterruptibly();  } catch (InterruptedException e) {  System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");  }  }, "Thread-2");  thread2.start();  // 中断第二个线程,因为它可能正在等待获取锁  Thread.sleep(500);  thread2.interrupt();  // 等待第一个线程完成  thread1.join();  }  
}

在上面代码中,Warehouse类有一个accessResourceInterruptibly方法,它使用lockInterruptibly()方法来获取锁,如果线程在等待获取锁的过程中被中断,它将抛出InterruptedException异常。

Client类的main方法中,创建了两个线程,第一个线程(thread1)启动后,让它先运行一段时间以确保它能够获取到锁,然后,启动第二个线程(thread2),并立即中断它,由于thread2在调用lockInterruptibly()时可能会被中断,因此它应该捕获InterruptedException并打印出一条消息,表明它在等待锁的过程中被中断了。

输出如下:

Thread-1 accessed the resource: Goods inside the warehouse  
Thread-2 was interrupted while waiting for the lock.

这个输出表明thread1成功访问了资源,而thread2则在等待获取锁的过程中被中断了。

核心总结

Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法 - 程序员古德

1、使用lock方法的情况:

当需要确保一段代码或一系列操作以原子方式执行,即在这段代码执行期间不会被其他线程干扰时,可以使用lock方法,lock方法会尝试获取锁,如果锁当前被其他线程持有,那么当前线程将被挂起(即阻塞),直到锁变得可用,这是一种基本的线程同步机制,用于防止多个线程同时访问共享资源,从而避免数据不一致和其他并发问题。

2、使用lockInterruptibly方法的情况:

与lock方法类似,lockInterruptibly方法也用于获取锁以保护临界区,但是,lockInterruptibly有一个额外的特性:它是可响应中断的,这意味着,如果当前线程在等待获取锁的过程中被中断(例如,通过调用Thread.interrupt方法),lockInterruptibly将不会无限期地等待下去,而是会立即抛出InterruptedException异常,这使得线程能够更灵活地处理中断,例如,可以在被中断时执行一些清理操作或通知其他线程。

3、总结:

如果需要确保代码块的原子性,并且不关心线程是否能够在等待锁时被中断,那么可以使用lock方法,但是,如果希望线程在等待锁时能够响应中断,那么应该使用lockInterruptibly方法。在处理可能被中断的长时间操作或需要更细粒度的中断控制时,lockInterruptibly通常是一个更好的选择。

关注我,每天学习互联网编程技术 - 程序员古德

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

倍福PLC控制器开发环境介绍

倍福PLC控制器是一款功能强大、易于使用的可编程逻辑控制器,广泛应用于各种工业自动化控制系统中。为了充分发挥倍福PLC控制器的功能,需要使用合适的开发环境。下面将介绍倍福PLC控制器的开发环境,主要包括软件安装与配置、工程创建与管理、编…

“超人练习法”系列08:ZPD 理论

01 先认识一个靓仔 看过 Lev Vygotsky 这个人的书吗?他是一位熟练心理学家,对人们习得技能的方式非常感兴趣,但他 37 岁的时候就因肺炎英年早逝了。 他认为社会环境对学习有关键性的作用,认为社会因素与个人因素的整合促成了学习…

element ui el-table展示列表,结合分页+过滤功能

vueelement-ui实现的列表展示&#xff0c;列表分页&#xff0c;列表筛选功能 1&#xff0c;分页器 el-table模块下面是分页器代码 <el-pagination></el-pagination> <el-table></el-table> <!-- 分页器 --><div class"block" st…

力扣每日一练(24-1-13)

如果用列表生成式&#xff0c;可以满足输出的型式&#xff0c;但是不满足题意&#xff1a; nums[:] [i for i in nums if i ! val]return len(nums) 题意要求是&#xff1a; 你需要原地修改数组&#xff0c;并且只使用O(1)的额外空间。这意味着我们不能创建新的列表&#xff…

【QT】标准对话框

目录 1 概述 2 QFileDialog对话框 1.选择打开一个文件 2.选择打开多个文件 3&#xff0e;选择已有目录 4&#xff0e;选择保存文件名 3 QColorDialog对话框 4 QFontDialog对话框 5 QInputDialog标准输入对话框 1.输入文字 2&#xff0e;输入整数 3&#xff0e;输入…

Python教程(23)——Python类中常用的特殊成员

在Python中&#xff0c;类特殊成员是指以双下划线开头和结尾的属性和方法&#xff0c;也被称为魔术方法&#xff08;Magic methods&#xff09;或特殊方法&#xff08;Special methods&#xff09;。这些特殊成员在类的定义中具有特殊的语法和功能&#xff0c;用于实现对象的特…

【PlantUML】-类图-布局,如何改变元素位置

写在前面 PlantUML属于自动布局。掌握好&#xff0c;是一件利器&#xff0c;掌握不好&#xff0c;就会不知其所以然。尤其在布局方面&#xff0c;因为它的布局可能会和你想的不太一样。本篇文章以例子为基础&#xff0c;简单地说几个在实际应用过程中摸索出来的原则。相信看完这…

【猫头虎分享】全面揭秘鸿蒙4.0:华为的技术革新与市场影响

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

逆变器3前级推免(高频变压器)

一节电池标压是在2.8V—4.2V之间&#xff0c;所以24V电压需要大概七节电池串联。七节电池电压大概在19.6V—29.4V之间。 从24V的电池逆变到到220V需要升压的过程。那么我们具体需要升压到多少&#xff1f; 市电AC220V是有效值电压&#xff0c;峰值电压是220V*1.414311V 如果…

ssm基于Javaweb的物流信息管理系统的设计与实现论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统物流信息管理难度大&#xff0c;容错率低&#xff0c;管理…

数据结构——排序算法之快速排序

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 前言&#xff1a; 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。 基本思想&…

opencv-4.8.0编译及使用

1 编译 opencv的编译总体来说比较简单&#xff0c;但必须记住一点&#xff1a;opencv的版本必须和opencv_contrib的版本保持一致。例如opencv使用4.8.0&#xff0c;opencv_contrib也必须使用4.8.0。 进入opencv和opencv_contrib的github页面后&#xff0c;默认看到的是git分支&…

NAS搭建NextCloud集成OnlyOffice

1、安装NextCloud&#xff08;如果总是中断就换个镜像源&#xff09; 2、创建容器 如果需要穿透选HOST 端口必须80 读写必须开 3、启动容器并配置&#xff0c;看图。 启动看日志&#xff0c;等启动完成再访问。首次启动大约5-10分钟左右。 成功后&#xff0c;我们正常进行安装…

学习selenium+python使用 XPath 表达式来实现找到目标元素时智能封装等待,执行测试代码启动Chrome浏览器后,地址栏只显示data;

背景 学习使用 XPath 表达式来实现找到目标元素时智能封装等待执行测试代码启动Chrome浏览器后&#xff0c;地址栏只显示data&#xff1b; 代码如下 import unittest from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from …

6.2 声音编辑工具GoldWave5简介(5)

6.2.4录制声音 利用Windows自带的“录音机”录制声音时&#xff0c;只能录制最大时长为1分钟的声音&#xff0c;而利用GoldWave5&#xff0c;可以录制时长长达277小时以上的声音&#xff0c;而且&#xff0c;录制完成后&#xff0c;还可以很方便地对声音进行处理、转换等操作。…

记录用python封装的第一个小程序

前言 我要封装的是前段时间复现的一个视频融合拼接的程序&#xff0c;现在我打算将他封装成exe程序&#xff0c;我在这里只记录一下我封装的过程&#xff0c;使用的是pyinstaller&#xff0c;具体的封装知识我就不多说了&#xff0c;可以参考我另一篇博客&#xff1a;将Python…

NLP技术在搜索推荐场景中的应用

NLP技术在搜索推荐中的应用非常广泛&#xff0c;例如在搜索广告的CTR预估模型中&#xff0c;NLP技术可以从语义角度提取一些对CTR预测有效的信息&#xff1b;在搜索场景中&#xff0c;也经常需要使用NLP技术确定展现的物料与搜索query的相关性&#xff0c;过滤掉相关性较差的物…

力扣2182.构造限制重复的字符串

思路&#xff1a;先记录每个字符的出现次数&#xff0c;构建一个新字符串&#xff0c;从尾取字符&#xff0c;每取一个该字符个数-1&#xff0c;若该字符已经取到有repeatLimit个&#xff0c;则递归取次大的字符&#xff0c;并对应字符个数-1&#xff0c;若没有次大字符了&…

Elasticsearch基础篇(七):分片大小修改和路由分配规则

Elasticsearch基础篇(七)&#xff1a;分片大小修改和路由分配规则1. 分片1.1 主分片&#xff08;Primary Shard&#xff09;1.2 副本分片&#xff08;Replica Shard&#xff09;1.3 分片路由&#xff08;Routing Shard&#xff09; 2. 分片分配的基本策略3. 分片写入验证3.1 数…

2024年前端最新面试题-vue3(持续更新中)

文章目录 前言正文什么是 MVVC什么是 MVVM什么是 SPA什么是SFC为什么 data 选项是一个函数Vue 组件通讯&#xff08;传值&#xff09;有哪些方式Vue 的生命周期方法有哪些如何理解 Vue 的单项数据流如何理解 Vue 的双向数据绑定Vue3的响应式原理是什么介绍一下 Vue 的虚拟 DOM介…