java Lock锁的使用

news/2024/11/8 2:44:26/

Lock接口

public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock();
Condition newCondition();
}

这 5 种方法分别是 lock()、tryLock()、tryLock(long time, TimeUnit unit) 和 lockInterruptibly()、 unlock()。

lock()

在线程获取锁的时如果锁被其它的线程获取了,就会进行等待,lock加锁和释放锁都必须以代码的形式写出来,是由我们自己释放锁。要有trycatch语 句,在try中获取资源,如果出现异常在catch中捕获,然后释放锁。
伪代码使用如下:

Lock lock = ...; lock.lock();
try{
}finally{ lock.unlock();
}

一定不要忘记在finally语句中释放锁,否则是非常危险的锁会得不到释放,一旦陷入死锁就 会造成很大的隐患。

tryLock()

它可以尝试获取当前的锁,如果其他线程没有被占用,就能获取成功,返回true,反之亦然。
为代码如下:

Lock lock = ...; 
if(lock.tryLock()) { 
try{//业务逻辑
}finally{ lock.unlock();
}
}else {}

有了这个强大的 tryLock()方法我们便可以解决死锁问题
如下面的例子:
死锁是多线程编程中一种比较复杂的问题,而使用锁可以一定程度上避免死锁的发生。下面是一个简单的Java Lock锁解决死锁的例子,使用ReentrantLocktryLock方法来避免死锁。
考虑两个资源ResourceAResourceB,两个线程分别需要访问这两个资源。如果线程1先获取ResourceA,然后尝试获取ResourceB,而线程2先获取ResourceB,然后尝试获取ResourceA,就可能发生死锁。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class DeadlockExample {private static class SharedResource {final Lock lock = new ReentrantLock();}private static class MyThread extends Thread {private final SharedResource resource1;private final SharedResource resource2;public MyThread(String name, SharedResource resource1, SharedResource resource2) {super(name);this.resource1 = resource1;this.resource2 = resource2;}@Overridepublic void run() {try {// 尝试获取第一个资源resource1.lock.lock();System.out.println(getName() + " acquired lock on " + resource1);// 等待一段时间模拟处理业务逻辑Thread.sleep(100);// 尝试获取第二个资源if (resource2.lock.tryLock()) {try {System.out.println(getName() + " acquired lock on " + resource2);// 执行业务逻辑} finally {resource2.lock.unlock();System.out.println(getName() + " released lock on " + resource2);}} else {System.out.println(getName() + " couldn't acquire lock on " + resource2 + ". Releasing lock on " + resource1);}} catch (InterruptedException e) {e.printStackTrace();} finally {resource1.lock.unlock();System.out.println(getName() + " released lock on " + resource1);}}}public static void main(String[] args) {SharedResource resourceA = new SharedResource();SharedResource resourceB = new SharedResource();// 创建两个线程,分别尝试获取不同的资源MyThread thread1 = new MyThread("Thread-1", resourceA, resourceB);MyThread thread2 = new MyThread("Thread-2", resourceB, resourceA);// 启动两个线程thread1.start();thread2.start();}
}

运行结果:

Thread-1 acquired lock on SharedResource@hash1
Thread-1 acquired lock on SharedResource@hash2
Thread-2 couldn't acquire lock on SharedResource@hash1. Releasing lock on SharedResource@hash2
Thread-1 released lock on SharedResource@hash2
Thread-1 released lock on SharedResource@hash1
Thread-2 acquired lock on SharedResource@hash2
Thread-2 acquired lock on SharedResource@hash1
Thread-2 released lock on SharedResource@hash1
Thread-2 released lock on SharedResource@hash2

这个输出说明线程1首先获取resource1的锁,然后等待一段时间,尝试获取resource2的锁。由于线程2已经获取了resource2的锁,线程1无法立即获取resource2的锁,因此释放了resource1的锁。随后,线程2成功获取了resource1的锁,完成了整个流程。

tryLock(long time, TimeUnit unit)

该方法可以设置超时时间,如果拿不到锁等待超了设置的时间后,线程就会放弃获取这把 锁。在等待的时间也可以中断线程,避免死锁的发生。
如下面的例子:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;public class TryLockExample {private static class SharedResource {final Lock lock = new ReentrantLock();}private static class MyThread extends Thread {private final SharedResource resource;public MyThread(String name, SharedResource resource) {super(name);this.resource = resource;}@Overridepublic void run() {try {if (resource.lock.tryLock(500, TimeUnit.MILLISECONDS)) {try {System.out.println(getName() + " acquired lock on " + resource);// 执行业务逻辑} finally {resource.lock.unlock();System.out.println(getName() + " released lock on " + resource);}} else {System.out.println(getName() + " couldn't acquire lock on " + resource + " within 500 milliseconds");

运行结果:

Thread-1 acquired lock on SharedResource@hash1
Thread-2 couldn't acquire lock on SharedResource@hash1 within 500 milliseconds
Thread-1 released lock on SharedResource@hash1

Thread-1首先获取了锁,而 Thread-2 在500毫秒内无法获取到锁,因此放弃锁的获取。
之后Thread-1释放了锁。

lockInterruptibly()

此方法的作用是获取锁,如果这个锁可以获得,那么该方法就会立刻返回。如果获取不到 被其他线程占用了,就会一直等待。
使用为代码:

public void lockInterruptibly() { try {
lock.lockInterruptibly();
try { System.out.println(“操作资源”); } finally {
lock.unlock();
}
} catch (InterruptedException e) { e.printStackTrace();
}
}

unlock()

就是解锁。
对于ReentrantLock执行此方法,内部就会把锁,持有锁的计数器减去1,当减去1为0时 候。此锁就被释放了。

总结

  1. 引入Lock接口: Lock 接口提供了比传统的synchronized关键字更为灵活和可扩展的锁定机制。在 java.util.concurrent.locks 包中定义了多个实现了 Lock 接口的类,其中最常用的是 ReentrantLock
  2. 获取锁: 使用 lock() 方法来获取锁。这一步类似于使用synchronized关键字进行同步,但提供了更灵活的控制。
lock.lock();
try {// 执行需要同步的代码块
} finally {lock.unlock();
}
  1. 释放锁:finally 块中使用 unlock() 方法释放锁,确保锁的释放,避免死锁和其他并发问题。
  2. 可中断性: 使用 lockInterruptibly() 方法可实现可中断的锁获取。在等待锁的过程中,线程可以被中断,以避免长时间的阻塞。
try {lock.lockInterruptibly(); // 可中断地获取锁// 执行需要同步的代码块
} catch (InterruptedException e) {// 处理中断异常
} finally {lock.unlock();
}
  1. 超时获取锁: 使用 tryLock(long time, TimeUnit unit) 方法,可以尝试在指定时间内获取锁。如果在指定时间内无法获取到锁,线程可以选择放弃或进行其他处理。
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {try {// 执行需要同步的代码块} finally {lock.unlock();}
} else {// 未能在指定时间内获取到锁
}

总的来说,Lock 提供了更多的控制和灵活性,适用于更复杂的并发场景。在使用时,需要注意合理释放锁,避免死锁,并根据具体需求选择合适的锁实现和锁策略。


http://www.ppmy.cn/news/1255106.html

相关文章

『Nginx安全访问控制』利用Nginx实现账号密码认证登录的最佳实践

📣读完这篇文章里你能收获到 如何创建用户账号和密码文件,并生成加密密码配置Nginx的认证模块,实现基于账号密码的登录验证 文章目录 一、创建账号密码文件1. 安装htpasswd工具1.1 CentOS1.2 Ubuntu 二、配置Nginx三、重启Nginx 在Web应用程…

C++设计模式——建造者模式(Builder)

一、什么是建造者模式? 建造者模式是一种创建型的软件设计模式,用于构造相对复杂的对象。 建造者模式可以将复杂对象的构建与它的表示分离,使得相同的构建过程可以得到不同的表示。如果说工厂模式和抽象工厂模式更注重产品整体,…

Vue组件分装之$attrs、$listener传递属性及事件

使用v-bind"$attrs"来将父组件的属性传递给自定义按钮 使用v-on"$listeners"将父组件的事件监听器传递给自定义按钮。 使用$slots获取父组件所有插槽以及作用域插槽对应的参数#[name]"scopeData" 这样,自定义按钮就能够直接响应父…

【大数据】区分 hdfs dfs -ls 与 hdfs dfs -ls /

😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 &#x…

296_C++_一个dialog对话框在执行exec向系统发送一个延后销毁事件时,另一个对话框立刻接管了上一个对话框的销毁事件,导致死UI

1、根因分析 -根因分析:当有新版本并且grade等级是2的时候,点击ptz的时候使用的是RSDialog,WA_DeleteOnClose属性默认是为true的, 并且是栈上的变量,当关闭ptz的时候,diolog的exec结束会向系统发送延后销毁事件,此时退出ptz会弹出自动升级对话框,接管了 事件循环,则会调用前面…

【数电笔记】16-卡诺图绘制(逻辑函数的卡诺图化简)

目录 说明: 最小项卡诺图的组成 1. 相邻最小项 2. 卡诺图的组成 2.1 二变量卡诺图 2.2 三表变量卡诺图 2.3 四变量卡诺图 3. 卡诺图中的相邻项(几何相邻) 说明: 笔记配套视频来源:B站;本系列笔记并…

大数据湖项目建设方案:文档全文101页,附下载

关键词:大数据解决方案,数据湖解决方案,数据治理解决方案,数据中台解决方案 一、大数据湖建设思路 1、明确目标和定位:明确大数据湖的目标和定位是整个项目的基础,这可以帮助我们确定项目的内容、规模、所…

07、基于LunarLander登陆器的强化学习案例(含PYTHON工程)

07、基于LunarLander登陆器的强化学习(含PYTHON工程) 开始学习机器学习啦,已经把吴恩达的课全部刷完了,现在开始熟悉一下复现代码。全部工程可从最上方链接下载。 基于TENSORFLOW2.10 0、实践背景 gym的LunarLander是一个用于…