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

Java 中 Lock 接口详解:灵活强大的线程同步机制

在 Java 中,Lock 是一个接口,它提供了比 synchronized 关键字更灵活、更强大的线程同步机制。以下将详细介绍 Lock 接口及其实现类,以及它与 synchronized 相比的优点。

Lock 接口及其实现类介绍

Lock 接口

Lock 接口定义了一系列用于获取和释放锁的方法,主要方法如下:

  • void lock():获取锁,如果锁不可用,则当前线程将被阻塞,直到锁被释放。
  • void lockInterruptibly():可中断地获取锁,在获取锁的过程中,如果当前线程被中断,则会抛出 InterruptedException 异常。
  • boolean tryLock():尝试非阻塞地获取锁,如果锁可用,则获取锁并返回 true;否则返回 false。
  • boolean tryLock(long time, TimeUnit unit):在指定的时间内尝试获取锁,如果在该时间内锁可用,则获取锁并返回 true;否则返回 false。如果在等待过程中线程被中断,则会抛出 InterruptedException 异常。
  • void unlock():释放锁。
  • Condition newCondition():返回一个与该锁关联的 Condition 对象,用于实现线程间的等待 - 通知机制。

常见实现类

  • ReentrantLock:可重入锁,是 Lock 接口最常用的实现类。它支持与 synchronized 相同的可重入特性,即同一个线程可以多次获取同一把锁而不会被阻塞。
  • ReentrantReadWriteLock:读写锁,它维护了一对锁,一个读锁和一个写锁。多个线程可以同时获取读锁,但写锁是排他的,即同一时间只能有一个线程获取写锁,并且在写锁被持有时,其他线程不能获取读锁或写锁。

Lock 与 synchronized 对比的优点

1. 灵活性更高
  • 锁的获取和释放可分离:synchronized 是基于代码块或方法的,锁的获取和释放是隐式的,由 JVM 自动完成。而 Lock 接口的 lock() 和 unlock() 方法可以在不同的代码块中调用,这使得锁的获取和释放更加灵活。
    例如,在某些复杂的业务逻辑中,可能需要在不同的条件下释放锁,使用 Lock 可以很方便地实现这一点。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockFlexibilityExample {private final Lock lock = new ReentrantLock();public void complexOperation() {lock.lock();try {// 执行一些操作if (someCondition()) {return;}// 继续执行其他操作} finally {lock.unlock();}}private boolean someCondition() {// 模拟条件判断return Math.random() > 0.5;}
}

在上述代码中,如果 someCondition() 方法返回 true,则会提前退出方法,但在 finally 块中仍然可以确保锁被释放。

  • 可中断的锁获取:Lock 接口提供了 lockInterruptibly() 方法,允许线程在获取锁的过程中被中断。而 synchronized 关键字在获取锁时是不可中断的,一旦线程进入阻塞状态,就只能等待锁被释放。这在某些场景下非常有用,例如当线程需要响应中断信号来执行其他操作时。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class InterruptibleLockExample {private final Lock lock = new ReentrantLock();public void interruptibleTask() throws InterruptedException {lock.lockInterruptibly();try {// 执行任务System.out.println("获取到锁,执行任务");} finally {lock.unlock();}}public static void main(String[] args) {InterruptibleLockExample example = new InterruptibleLockExample();Thread thread = new Thread(() -> {try {example.interruptibleTask();} catch (InterruptedException e) {System.out.println("线程被中断");}});thread.start();// 中断线程thread.interrupt();}
}

在上述代码中,线程在获取锁的过程中被中断,会抛出 InterruptedException 异常,从而可以进行相应的处理。

2. 可实现公平锁

ReentrantLock 可以通过构造函数指定是否为公平锁。公平锁是指线程按照请求锁的顺序依次获取锁,避免了某些线程长时间得不到锁的情况。而 synchronized 关键字是非公平锁,线程获取锁的顺序是不确定的,可能会导致某些线程饥饿。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class FairLockExample {private final Lock fairLock = new ReentrantLock(true);public void fairTask() {fairLock.lock();try {// 执行任务System.out.println(Thread.currentThread().getName() + " 获取到公平锁");} finally {fairLock.unlock();}}public static void main(String[] args) {FairLockExample example = new FairLockExample();for (int i = 0; i < 5; i++) {new Thread(example::fairTask, "Thread-" + i).start();}}
}

在上述代码中,ReentrantLock 被初始化为公平锁,线程会按照请求锁的顺序依次获取锁。

3. 支持多个条件变量

Lock 接口的 newCondition() 方法可以返回一个与该锁关联的 Condition 对象,一个 Lock 可以关联多个 Condition 对象,从而实现更精细的线程间通信。而 synchronized 关键字只能使用 wait()、notify() 和 notifyAll() 方法,这些方法是基于对象的监视器,只能实现简单的线程间等待 - 通知机制。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ConditionExample {private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();private final int[] buffer = new int[10];private int count = 0;private int in = 0;private int out = 0;public void put(int value) throws InterruptedException {lock.lock();try {while (count == buffer.length) {notFull.await();}buffer[in] = value;in = (in + 1) % buffer.length;count++;notEmpty.signal();} finally {lock.unlock();}}public int take() throws InterruptedException {lock.lock();try {while (count == 0) {notEmpty.await();}int value = buffer[out];out = (out + 1) % buffer.length;count--;notFull.signal();return value;} finally {lock.unlock();}}
}

在上述代码中,notFull 和 notEmpty 是两个不同的 Condition 对象,分别用于控制缓冲区满和缓冲区空的情况,实现了更精细的线程间通信。
总结
Lock 接口及其实现类提供了比 synchronized 关键字更灵活、更强大的线程同步机制,在需要更精细的锁控制、可中断的锁获取、公平锁和多个条件变量等场景下,Lock 是更好的选择。但 synchronized 关键字使用起来更加简单,在一些简单的同步场景下仍然是首选。

相关文章:

  • AI笔记 - 网络模型 - mobileNet
  • 【沉浸式求职学习day51】【发送邮件】【javaweb结尾】
  • Python 爱心图案代码
  • [SC]SystemC在CPU/GPU验证中的应用(五)
  • 【项目】在线OJ(负载均衡式)
  • 微服务中引入公共拦截器
  • GB 36246-2018 中小学合成材料面层运动场地检测
  • C++ 观察者模式:设计与实现详解
  • 建造者模式:优雅构建复杂对象
  • 飞腾D2000与FPGA结合的主板
  • FPGA纯verilog实现MIPI-DSI视频编码输出,提供工程源码和技术支持
  • proteus美观与偏好设置
  • 鲲鹏Arm+麒麟V10 K8s 离线部署教程
  • git下载和安装(完整版)
  • 【Docker项目实战篇】Docker部署PDF查看器PdfDing
  • 【Dv3Admin】工具权限配置文件解析
  • 机器学习:欠拟合、过拟合、正则化
  • Qt -使用OpenCV得到SDF
  • MacOS安装Docker Desktop并汉化
  • [001]从操作系统层面看锁的逻辑
  • 网站设计宽屏尺寸/搜索引擎推广的费用
  • 哈尔滨快速建站专业定制/如何优化搜索引擎
  • 无锡网站推广$做下拉去118cr/今日新闻摘抄十条
  • 怎么做彩票游戏网站/深圳搜索引擎
  • 楼盘网站建设案例/网络营销与电子商务的区别
  • 怎样突破网站设计瓶颈/十大搜索引擎网站