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

Java 中 synchronized 和 ReentrantLock 的全面对比解析

synchronized 和 ReentrantLock 都是 Java 中用于实现线程同步的机制,它们的主要目的都是确保在多线程环境下对共享资源的安全访问,但在多个方面存在差异。下面将从多个维度对二者进行详细对比解析。

语法与使用方式

synchronized

synchronized 是 Java 的关键字,使用起来较为简洁,有以下三种使用方式:

同步实例方法

修饰实例方法时,锁对象是当前实例。

public class SynchronizedInstanceMethod {private int count = 0;public synchronized void increment() {count++;}
}

同步静态方法

修饰静态方法时,锁对象是当前类的 Class 对象。

public class SynchronizedStaticMethod {private static int count = 0;public static synchronized void increment() {count++;}
}

同步代码块

可以指定锁对象。

public class SynchronizedBlock {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}}
}

锁的获取和释放由 JVM 自动处理,开发者无需手动干预。

ReentrantLock

ReentrantLock 是 java.util.concurrent.locks 包下的一个类,使用时需要手动获取和释放锁,通常结合 try-finally 语句确保锁一定会被释放。

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}

锁的特性

可重入性

二者都具备可重入性,即同一个线程可以多次获取同一把锁而不会被阻塞。

  • synchronized:当一个线程已经获取了某个对象的锁,再次请求该对象的锁时可以直接获得。
public class SynchronizedReentrant {public synchronized void method1() {System.out.println("Method 1");method2();}public synchronized void method2() {System.out.println("Method 2");}
}
  • ReentrantLock:同样支持可重入,重入次数会被记录,每次释放锁时重入次数减 1,直到重入次数为 0 时锁才真正被释放。
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockReentrant {private final ReentrantLock lock = new ReentrantLock();public void method1() {lock.lock();try {System.out.println("Method 1");method2();} finally {lock.unlock();}}public void method2() {lock.lock();try {System.out.println("Method 2");} finally {lock.unlock();}}
}

公平性

  • synchronized:是非公平锁,线程获取锁的顺序是不确定的,可能会导致某些线程长时间得不到锁,出现线程饥饿现象。
  • ReentrantLock:可以通过构造函数指定是否为公平锁。公平锁会按照线程请求锁的顺序依次获取锁,避免了线程饥饿问题,但公平锁的性能相对较低。
import java.util.concurrent.locks.ReentrantLock;public class FairReentrantLock {private final ReentrantLock fairLock = new ReentrantLock(true);public void doSomething() {fairLock.lock();try {// 执行操作} finally {fairLock.unlock();}}
}

锁的获取与释放控制

synchronized

锁的获取和释放是隐式的,由 JVM 自动完成。当线程进入 synchronized 修饰的方法或代码块时,自动获取锁;当线程退出时,自动释放锁。开发者无法控制锁的获取和释放时机。

ReentrantLock

提供了更灵活的锁控制:

  • 可中断的锁获取:可以使用 lockInterruptibly() 方法在获取锁的过程中响应中断。
import java.util.concurrent.locks.ReentrantLock;public class InterruptibleLock {private final ReentrantLock lock = new ReentrantLock();public void doTask() throws InterruptedException {lock.lockInterruptibly();try {// 执行任务} finally {lock.unlock();}}
}
  • 尝试非阻塞获取锁:使用 tryLock() 方法尝试获取锁,如果锁可用则获取锁并返回 true,否则返回 false,不会阻塞线程。还可以使用 tryLock(long timeout, TimeUnit unit) 方法在指定时间内尝试获取锁。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;public class TryLockExample {private final ReentrantLock lock = new ReentrantLock();public void tryToLock() {if (lock.tryLock()) {try {// 执行操作} finally {lock.unlock();}} else {// 锁不可用,执行其他操作}}public void tryToLockWithTimeout() throws InterruptedException {if (lock.tryLock(1, TimeUnit.SECONDS)) {try {// 执行操作} finally {lock.unlock();}} else {// 超时未获取到锁,执行其他操作}}
}

条件变量支持

synchronized

只能使用 wait()、notify() 和 notifyAll() 方法进行线程间的等待 - 通知机制,这些方法是基于对象的监视器,功能相对有限。

public class SynchronizedWaitNotify {private final Object lock = new Object();private boolean condition = false;public void waitForCondition() throws InterruptedException {synchronized (lock) {while (!condition) {lock.wait();}// 条件满足,执行操作}}public void signalCondition() {synchronized (lock) {condition = true;lock.notifyAll();}}
}
ReentrantLock

通过 newCondition() 方法可以返回一个与该锁关联的 Condition 对象,一个 ReentrantLock 可以关联多个 Condition 对象,实现更精细的线程间通信。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockCondition {private final ReentrantLock 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();}}
}

性能差异

synchronized

在早期版本中,synchronized 的性能较差,因为它使用的是重量级锁,线程的阻塞和唤醒需要操作系统的介入。但在 Java 6 之后,JVM 对 synchronized 进行了大量优化,引入了偏向锁、轻量级锁等机制,在并发程度不高的情况下,性能与 ReentrantLock 相差不大。

ReentrantLock

在高并发场景下,ReentrantLock 可以通过更灵活的锁控制和可中断、尝试获取锁等特性,避免线程长时间阻塞,从而提高性能。但如果使用不当,手动管理锁的开销可能会导致性能下降。

总结

  • synchronized:使用简单,适合对代码简洁性要求较高、并发程度不是特别高的场景,JVM 会自动处理锁的获取和释放。
  • ReentrantLock:功能更强大,提供了更灵活的锁控制、公平锁、可中断锁获取和多个条件变量等特性,适合对锁的控制要求较高、并发场景复杂的情况,但需要手动管理锁的获取和释放。

相关文章:

  • LeetCode hot100---152.乘机最大子数组
  • Protobuf 中的类型查找规则
  • MS358A 低功耗运算放大器 车规
  • 在 Windows 11 或 10 上将 Git 升级到最新版本的方法
  • Linux【4】------RK3568启动和引导顺序
  • JAVA理论第五章-JVM
  • ubuntu服务器件如何配置python环境并运行多个python脚本
  • Ubuntu20.04基础配置安装——系统安装(一)
  • 应急响应思路
  • 【超详细】英伟达Jetson Orin NX-YOLOv8配置与TensorRT测试
  • 深入理解 Vue.observable:轻量级响应式状态管理利器
  • Vue 项目实战:三种方式实现列表→详情页表单数据保留与恢复
  • UOS 20 Pro为国际版WPS设置中文菜单
  • iOS、Android、鸿蒙、Web、桌面 多端开发框架Kotlin Multiplatform
  • Redis主从复制的原理一 之 概述
  • 数字通信复习
  • Kafka 消息模式实战:从简单队列到流处理(二)
  • C#:发送一封带有附件的邮件
  • SQL Server 日期时间类型全解析:从精确存储到灵活转换
  • 表单设计器拖拽对象时添加属性
  • 带注册登录的网站模板/免费优化
  • 肇庆制作网站软件/快速提高网站关键词排名优化
  • 在网站做博客/网站统计分析工具的主要功能
  • 门户网站模板图片/济南百度竞价开户
  • 二手网站建设/微信营销的模式有哪些
  • 使用QQ做网站客服/国外搜索引擎排名百鸣