如何在Java中使用锁?

在Java中通过synchronized关键字或Lock接口实现线程同步,synchronized可修饰方法或代码块,自动管理锁;Lock需显式调用lock()/unlock(),提供更灵活的加锁机制,如尝试获取锁、超时控制等,确保多线程安全访问共享资源。

为什么需要锁?

当多个线程同时读写共享数据(如静态变量、对象属性)时,可能引发:

如何在Java中使用锁?

  • 数据竞争:线程A修改数据时,线程B同时读取导致脏数据。
  • 状态不一致:非原子操作(如i++包含读-改-写三步)被线程交替执行。
    锁通过互斥性确保同一时刻仅一个线程访问资源,并保证修改后的值对其他线程可见

内置锁:synchronized

synchronized是Java最基础的锁机制,JVM自动管理锁的获取与释放。

同步代码块

public class Counter {
    private int count = 0;
    private final Object lock = new Object(); // 锁对象
    public void increment() {
        synchronized (lock) { // 对lock对象加锁
            count++; // 临界区代码
        }
    }
}
  • 锁对象:可以是任意对象(如lock),推荐使用私有final对象避免外部干扰。
  • 作用范围:仅包裹需要互斥的代码(临界区),减少锁竞争。

同步方法

public synchronized void increment() { 
    count++; // 等同于锁住当前对象实例(this)
}
public static synchronized void staticMethod() { 
    // 锁住当前类的Class对象(Counter.class)
}
  • 实例方法:锁对象是当前实例(this)。
  • 静态方法:锁对象是类的Class对象(如Counter.class)。

显式锁:Lock接口

java.util.concurrent.locks.Lock提供更灵活的锁控制,需手动释放锁,常用实现类为ReentrantLock

基础用法

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SafeCounter {
    private final Lock lock = new ReentrantLock(); // 创建显式锁
    private int value = 0;
    public void add(int num) {
        lock.lock(); // 获取锁
        try {
            value += num; // 临界区
        } finally {
            lock.unlock(); // 必须在finally中释放锁
        }
    }
}
  • 优势:支持尝试获取锁、超时锁、可中断锁等高级功能。
  • 注意unlock()必须放在finally块中,防止异常导致锁未释放。

高级功能

  • 尝试锁(tryLock):避免线程阻塞

    if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试等待1秒
        try { /* 操作资源 */ } 
        finally { lock.unlock(); }
    } else {
        System.out.println("获取锁失败,执行备选逻辑");
    }
  • 可中断锁:响应线程中断

    如何在Java中使用锁?

    public void interruptibleLock() throws InterruptedException {
        lock.lockInterruptibly(); // 若线程被中断,抛出InterruptedException
        try { /* ... */ } 
        finally { lock.unlock(); }
    }
  • 公平锁:减少线程饥饿

    Lock fairLock = new ReentrantLock(true); // true表示公平锁

    公平锁按请求顺序分配锁,但可能降低吞吐量(默认非公平锁)。


锁的选择策略

场景 推荐锁类型 理由
简单同步需求(如单方法互斥) synchronized 代码简洁,JVM自动优化
需要超时/尝试锁、可中断锁 ReentrantLock 灵活控制锁行为
高并发且锁竞争激烈 ReentrantLock 结合tryLock可避免线程阻塞
读多写少场景 ReentrantReadWriteLock 读写分离提升并发性(非本文重点)

避坑指南

  1. 死锁预防

    • 避免嵌套锁:线程A持有锁1请求锁2,线程B持有锁2请求锁1。
    • 统一加锁顺序:所有线程按固定顺序获取锁(如先锁A再锁B)。
    • 使用tryLock设置超时。
  2. 锁粒度优化

    如何在Java中使用锁?

    • 细化锁:锁住最小必要代码块(如synchronized块优于同步方法)。
    • 分离锁:对无关联资源使用不同锁对象(如lockForDatalockForLog)。
  3. 性能考量

    • 避免在锁中执行耗时操作(如IO操作)。
    • 优先使用线程安全类(如ConcurrentHashMap代替synchronized+HashMap)。

  • 基础场景:优先用synchronized,简洁安全。
  • 复杂需求:选择ReentrantLock,利用其超时、中断等特性增强健壮性。
  • 核心原则:锁范围最小化、避免嵌套、及时释放。

引用说明: 参考Oracle官方文档Java Concurrency、Brian Goetz所著《Java并发编程实战》(Java Concurrency in Practice),以及Java API文档Lock接口,实践代码基于JDK 8+验证。

原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/35477.html

(0)
酷盾叔的头像酷盾叔
上一篇 2025年6月22日 17:41
下一篇 2025年6月22日 17:54

相关推荐

  • Java中如何关闭窗口?

    在Java中关闭窗口可通过调用dispose()方法实现,frame.dispose(),对于Swing的JFrame,也可设置默认关闭行为:setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE),监听WindowEvent并执行System.exit(0)`能彻底终止程序。

    2025年6月19日
    400
  • Java如何实现二进制位运算计算?

    在Java中计算二进制可使用位运算符(如&、|、^)进行运算,或通过Integer.toBinaryString()将整数转为二进制字符串,还可用BigInteger处理超大二进制值,BitSet进行位操作,int result = a & b; 或 String binary = Integer.toBinaryString(10);

    2025年6月4日
    200
  • java怎么设置系统音量

    Java中,可通过调用系统命令或使用JNA等方式设置系统音量,但需注意不同操作系统的实现方式有所不同

    2025年7月13日
    000
  • java 怎么计算除法

    va中计算除法,整数除法用“/”运算符,结果为整数,舍去小数;浮点数除法需至少一个操作数为浮点数,如double或float类型

    2025年7月22日
    000
  • Java就业需求大吗

    Java在招聘市场中需求广泛,岗位数量较多,作为主流企业级开发语言,其在金融、电商、后端服务等领域应用普遍,技术生态成熟,因此企业持续招聘Java开发人员,就业机会相对丰富。

    2025年6月7日
    100

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-880-8834

在线咨询: QQ交谈

邮件:HI@E.KD.CN