Java ReentrantLock 核心用法
ReentrantLock
是 Java 中处理并发问题的强大工具,用于解决多线程下的竞态条件(如 count++
操作导致的数据不一致)。本文旨在展示其最核心、最标准的用法。
1. 实战代码示例
以下代码通过对比加锁与否,直观展示 ReentrantLock
的作用。
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. 实例化一个 ReentrantLockprivate 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. 运行结果
--- 场景一:不使用锁 (线程不安全) ---
预期结果: 100000
实际结果: 99654
---------------------------------------- 场景二:使用 ReentrantLock (线程安全) ---
预期结果: 100000
实际结果: 100000
-------------------------------------
结果清晰地表明,不使用锁导致了数据错误,而 ReentrantLock
保证了计算的正确性。
3. 核心使用范式
ReentrantLock
的使用必须遵循以下结构,以确保锁在任何情况下(包括异常)都能被释放,从而防止死锁。
lock.lock(); // 1. 获取锁
try {// 2. 执行需要保护的代码
} finally {lock.unlock(); // 3. 在 finally 块中释放锁
}
结论: 与 synchronized
的自动锁管理不同,ReentrantLock
需要开发者手动保证锁的释放。严格遵守 try-finally
范式是正确使用 ReentrantLock
的关键。