上一篇                     
               
			  如何在Java中使用锁?
- 后端开发
- 2025-06-22
- 4604
 在Java中通过synchronized关键字或Lock接口实现线程同步,synchronized可修饰方法或代码块,自动管理锁;Lock需显式调用lock()/unlock(),提供更灵活的加锁机制,如尝试获取锁、超时控制等,确保多线程安全访问共享资源。
 
为什么需要锁?
当多个线程同时读写共享数据(如静态变量、对象属性)时,可能引发:
- 数据竞争:线程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("获取锁失败,执行备选逻辑"); }
-  可中断锁:响应线程中断 public void interruptibleLock() throws InterruptedException { lock.lockInterruptibly(); // 若线程被中断,抛出InterruptedException try { /* ... */ } finally { lock.unlock(); } }
-  公平锁:减少线程饥饿 Lock fairLock = new ReentrantLock(true); // true表示公平锁 公平锁按请求顺序分配锁,但可能降低吞吐量(默认非公平锁)。  
锁的选择策略
| 场景 | 推荐锁类型 | 理由 | 
|---|---|---|
| 简单同步需求(如单方法互斥) | synchronized | 代码简洁,JVM自动优化 | 
| 需要超时/尝试锁、可中断锁 | ReentrantLock | 灵活控制锁行为 | 
| 高并发且锁竞争激烈 | ReentrantLock | 结合 tryLock可避免线程阻塞 | 
| 读多写少场景 | ReentrantReadWriteLock | 读写分离提升并发性(非本文重点) | 
避坑指南
-  死锁预防: - 避免嵌套锁:线程A持有锁1请求锁2,线程B持有锁2请求锁1。
- 统一加锁顺序:所有线程按固定顺序获取锁(如先锁A再锁B)。
- 使用tryLock设置超时。
 
-  锁粒度优化: - 细化锁:锁住最小必要代码块(如synchronized块优于同步方法)。
- 分离锁:对无关联资源使用不同锁对象(如lockForData和lockForLog)。
 
- 细化锁:锁住最小必要代码块(如
-  性能考量:  - 避免在锁中执行耗时操作(如IO操作)。
- 优先使用线程安全类(如ConcurrentHashMap代替synchronized+HashMap)。
 
- 基础场景:优先用synchronized,简洁安全。
- 复杂需求:选择ReentrantLock,利用其超时、中断等特性增强健壮性。
- 核心原则:锁范围最小化、避免嵌套、及时释放。
引用说明: 参考Oracle官方文档Java Concurrency、Brian Goetz所著《Java并发编程实战》(Java Concurrency in Practice),以及Java API文档Lock接口,实践代码基于JDK 8+验证。
 
  
			 
			 
			 
			 
			 
			