Lock锁

核心概念

  1. Lock 接口

    • 定义锁的基本操作:lock()、unlock()、tryLock() 等。
    • 替代 synchronized 的显式锁机制,支持更细粒度的控制。
  2. ReentrantLock(可重入锁)

    • 最常见的 Lock 实现类,允许同一线程多次获取同一把锁(避免死锁)。

基本用法

java 复制代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock(); // 获取锁
        try {
            counter++;
        } finally {
            lock.unlock(); // 确保释放锁
        }
    }
}

Lock 与 synchronized 的区别

1. 实现方式
  • synchronized

    • JVM 内置关键字:通过对象头的锁状态(偏向锁→轻量级锁→重量级锁)实现,由 JVM 自动管理。
    • 隐式加锁:无需手动释放锁,代码块或方法执行完毕自动释放。
  • Lock

    • Java API 接口 (如 ReentrantLock):基于 AQS(AbstractQueuedSynchronizer)实现,需显式调用 lock() 和 unlock()。
    • 显式加锁 :必须手动释放锁(通常放在 finally 块中),否则可能导致死锁。
2. 锁特性
特性 synchronized Lock
公平性 仅支持非公平锁 支持公平锁和非公平锁(构造参数指定)
中断响应 不支持中断等待线程 支持 lockInterruptibly() 中断等待
超时机制 不支持 支持 tryLock(timeout) 设定超时时间
条件变量 单一条件(wait()/notify()) 支持多个 Condition 对象,精细化线程唤醒
锁状态查询 无法判断锁是否被占用 支持 isLocked() 等方法查询锁状态
3. 性能差异
  • 低竞争场景 :
    synchronized 性能更优(JVM 的偏向锁、轻量级锁优化减少开销)。
  • 高竞争场景 :
    Lock 性能更好(基于 CAS 自旋减少线程阻塞,支持更细粒度的锁控制)。
4. 锁类型
  • 均为悲观锁 :
    两者都假设并发冲突必然发生 ,访问共享资源前先加锁(synchronized 直接阻塞,Lock 可能自旋后阻塞)。
  • 乐观锁 是另一种机制(如 CAS 或版本号),无需加锁,通过冲突检测实现线程安全。
5. 使用场景
  • 优先 synchronized:

    • 简单同步需求(如单例模式、简单代码块)。
    • 低线程竞争场景(利用 JVM 锁优化)。
  • 优先 Lock:

    • 需要复杂控制(如超时、中断、公平锁)。
    • 高并发场景(减少线程阻塞,提升吞吐量)。
    • 需要绑定多个条件变量(如生产者-消费者模型)。
总结
  • synchronized:简单、自动管理,适合基础同步需求。
  • Lock:灵活、功能强大,适合高并发和复杂场景。
  • 选择依据 :优先 synchronized,复杂需求或性能瓶颈时改用 Lock。

高级功能

  1. 尝试获取锁 (tryLock())

    java 复制代码
    if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试在1秒内获取锁
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
    } else {
        // 超时处理
    }
  2. 公平锁

    java 复制代码
    Lock fairLock = new ReentrantLock(true); // 公平锁,按等待顺序分配锁
  3. 条件变量 (Condition)

    java 复制代码
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    // 线程等待条件
    lock.lock();
    try {
        condition.await(); // 释放锁并等待,类似 wait()
    } finally {
        lock.unlock();
    }
    
    // 唤醒等待线程
    lock.lock();
    try {
        condition.signal(); // 类似 notify()
    } finally {
        lock.unlock();
    }

读写锁 (ReentrantReadWriteLock)

  • 适用于读多写少场景,提高并发性能:

    java 复制代码
    ReadWriteLock rwLock = new ReentrantReadWriteLock();
    Lock readLock = rwLock.readLock(); // 读锁(共享)
    Lock writeLock = rwLock.writeLock(); // 写锁(独占)
    
    public void readData() {
        readLock.lock();
        try {
            // 读操作
        } finally {
            readLock.unlock();
        }
    }
    
    public void writeData() {
        writeLock.lock();
        try {
            // 写操作
        } finally {
            writeLock.unlock();
        }
    }

注意事项

  1. 必须在 finally 中释放锁:避免因异常导致锁无法释放。
  2. 避免死锁:确保锁的获取和释放顺序一致。
  3. 性能考量 :高竞争场景下,synchronized 经过 JVM 优化后性能接近 Lock,但 Lock 提供更多控制选项。

适用场景

  • 需要细粒度控制锁(如超时、可中断)。
  • 需要公平锁或读写锁。
  • 需要多个条件变量实现复杂线程协作。

通过合理使用 Lock,可以显著提升多线程程序的灵活性和性能。

相关推荐
奇牙coding16 分钟前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!22 分钟前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
Lhappy嘻嘻29 分钟前
读懂 JVM:内存模型、类加载机制、分代回收
java·开发语言·jvm
黄俊懿29 分钟前
【架构师从入门到进阶】第六章:服务集群优化——第八节:线程数公式
java·服务器·数据库·分布式·性能优化·架构·系统架构
Rain的Java大神之路36 分钟前
别再死磕 MySQL 了:向量数据库与 RAG 生产落地实录(Java 工程师专场)
java·后端·面试
七夜zippoe37 分钟前
Spring AI 是什么?Java 工程师的 AI 开发第一站
java·人工智能·spring·ai·开发
是真的小外套1 小时前
2023年软考中级信息安全工程师|选择题考点+5道案例分析真题解析(含答案)
java·服务器·网络
会编程的吕洞宾1 小时前
Spring AI 2.0 结构化输出实战:让大模型返回精准 JSON 不再“抽风“,准确率从 78% 拉到 99%
java·后端
项目題供诗1 小时前
ai大模型(三)
java
wangjialelele1 小时前
一文梳理 Spring Cloud 核心组件:Eureka、Nacos、OpenFeign、LoadBalancer 与 Gateway
java·spring boot·spring·spring cloud·微服务·nacos