ReentrantLock
是 Java 中处理并发问题的强大工具,用于解决多线程下的竞态条件(如 count++
操作导致的数据不一致)。本文旨在展示其最核心、最标准的用法。
1. 实战代码示例
以下代码通过对比加锁与否,直观展示 ReentrantLock
的作用。
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// 1. 实例化一个 ReentrantLock
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
/**
* 不使用锁的递增方法(线程不安全)
*/
public void incrementWithoutLock() {
count++;
}
/**
* 使用 ReentrantLock 的递增方法(线程安全)
*/
public void incrementWithLock() {
// 2. 获取锁
lock.lock();
try {
// 3. 在 try 块中执行所有共享资源操作
count++;
} finally {
// 4. 在 finally 块中释放锁,确保锁一定会被释放
lock.unlock();
}
}
public int getCount() {
return count;
}
public void reset() {
count = 0;
}
public static void main(String[] args) throws InterruptedException {
final int threadCount = 100;
final int incrementsPerThread = 1000;
final int expectedCount = threadCount * incrementsPerThread;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
ReentrantLockDemo demo = new ReentrantLockDemo();
// --- 场景一:不使用锁 ---
System.out.println("--- 场景一:不使用锁 (线程不安全) ---");
for (int i = 0; i < threadCount; i++) {
for (int j = 0; j < incrementsPerThread; j++) {
executor.submit(demo::incrementWithoutLock);
}
}
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("预期结果: " + expectedCount);
System.out.println("实际结果: " + demo.getCount());
System.out.println("-------------------------------------\n");
// --- 场景二:使用 ReentrantLock ---
System.out.println("--- 场景二:使用 ReentrantLock (线程安全) ---");
demo.reset();
executor = Executors.newFixedThreadPool(threadCount);
for (int i = 0; i < threadCount; i++) {
for (int j = 0; j < incrementsPerThread; j++) {
executor.submit(demo::incrementWithLock);
}
}
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("预期结果: " + expectedCount);
System.out.println("实际结果: " + demo.getCount());
System.out.println("-------------------------------------");
}
}
2. 运行结果
markdown
--- 场景一:不使用锁 (线程不安全) ---
预期结果: 100000
实际结果: 99654
-------------------------------------
--- 场景二:使用 ReentrantLock (线程安全) ---
预期结果: 100000
实际结果: 100000
-------------------------------------
结果清晰地表明,不使用锁导致了数据错误,而 ReentrantLock
保证了计算的正确性。
3. 核心使用范式
ReentrantLock
的使用必须遵循以下结构,以确保锁在任何情况下(包括异常)都能被释放,从而防止死锁。
java
lock.lock(); // 1. 获取锁
try {
// 2. 执行需要保护的代码
} finally {
lock.unlock(); // 3. 在 finally 块中释放锁
}
结论: 与 synchronized
的自动锁管理不同,ReentrantLock
需要开发者手动保证锁的释放。严格遵守 try-finally
范式是正确使用 ReentrantLock
的关键。