Java代码中锁的使用
在Java并发编程中,锁是一种重要的同步机制,用于控制多个线程对共享资源的访问,避免数据不一致和竞争条件,锁的正确使用能够有效保证线程安全,但若使用不当,则可能导致死锁、性能下降等问题,本文将详细介绍Java中锁的类型、使用方法及最佳实践。

锁的基本概念
锁是一种同步工具,用于保护共享资源,当线程访问被锁保护的资源时,必须先获取锁;访问完成后释放锁,以便其他线程获取,锁的核心特性包括:
- 互斥性:同一时间只有一个线程能持有锁。
- 可重入性:同一线程可多次获取已持有的锁(避免死锁)。
- 公平性:锁的分配策略(公平锁按请求顺序分配,非公平锁允许插队)。
Java中锁的实现分为两种:synchronized关键字和java.util.concurrent.locks包中的显式锁(如ReentrantLock)。
synchronized关键字
synchronized是Java内置的同步机制,使用简单但功能相对基础。
使用方式
- 修饰实例方法:锁当前对象实例(
this)。public synchronized void instanceMethod() { // 同步代码块 } - 修饰静态方法:锁当前类的
Class对象。public static synchronized void staticMethod() { // 同步代码块 } - 修饰代码块:可指定锁对象,灵活性更高。
public void blockMethod() { synchronized (this) { // 同步代码块 } }
特点
- 自动释放锁:线程异常或执行完毕后自动释放锁。
- 非公平锁:默认为非公平锁,可能导致线程饥饿。
- 不可中断:线程获取锁时不可被中断(除非使用
Lock的lockInterruptibly)。
显式锁(ReentrantLock)
ReentrantLock是java.util.concurrent.locks包下的类,功能比synchronized更强大,支持公平锁、可中断锁、超时锁等。

基本使用
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final ReentrantLock lock = new ReentrantLock();
public void perform() {
lock.lock(); // 获取锁
try {
// 同步代码块
} finally {
lock.unlock(); // 释放锁(必须放在finally中)
}
}
}
高级特性
-
公平锁:通过构造函数参数指定公平性。
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
-
可中断锁:允许线程在等待时响应中断。
try { lock.lockInterruptibly(); // 同步代码块 } catch (InterruptedException e) { // 处理中断 } finally { lock.unlock(); } -
超时锁:尝试获取锁,超时后返回
false。if (lock.tryLock(1, TimeUnit.SECONDS)) { try { // 同步代码块 } finally { lock.unlock(); } } -
条件变量:通过
newCondition()创建条件,实现线程等待/通知机制。
ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 等待 lock.lock(); try { condition.await(); // 释放锁并等待 } finally { lock.unlock(); } // 通知 lock.lock(); try { condition.signal(); // 唤醒等待线程 } finally { lock.unlock(); }
读写锁(ReentrantReadWriteLock)
ReentrantReadWriteLock是一种分离锁,分为读锁(共享锁)和写锁(排他锁),适用于读多写少的场景。
使用示例
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private int value = 0;
// 读操作(共享锁)
public int read() {
rwLock.readLock().lock();
try {
return value;
} finally {
rwLock.readLock().unlock();
}
}
// 写操作(排他锁)
public void write(int newValue) {
rwLock.writeLock().lock();
try {
value = newValue;
} finally {
rwLock.writeLock().unlock();
}
}
}
特点
- 读锁共享:多个线程可同时持有读锁。
- 写锁独占:写锁与读锁/写锁互斥。
- 降级锁:持有写锁时可获取读锁(反之不行)。
锁的最佳实践
- 避免锁竞争:尽量缩小同步代码块范围,减少锁持有时间。
- 选择合适的锁类型:
- 简单同步用
synchronized;复杂场景用ReentrantLock。 - 读多写少用
ReentrantReadWriteLock。
- 简单同步用
- 防止死锁:
- 按固定顺序获取多个锁。
- 使用
tryLock避免无限等待。
- 释放锁:确保锁在
finally块中释放,避免异常导致锁泄漏。 - 考虑性能:公平锁可能降低吞吐量,非公平锁默认性能更好。
替代方案
除了锁,Java还提供了无锁并发工具,如:
Atomic类:基于CAS(Compare-And-Swap)实现原子操作。volatile关键字:保证可见性,但不保证原子性。StampedLock:ReentrantReadWriteLock的优化版本,支持乐观读。
锁是Java并发编程的核心工具,合理使用锁能够有效保证线程安全。synchronized适合简单场景,而ReentrantLock和ReentrantReadWriteLock提供了更灵活的控制,开发者需根据实际需求选择锁类型,并遵循最佳实践,避免常见问题如死锁和性能瓶颈,通过深入理解锁的机制,可以编写出高效、健壮的并发程序。


















