Java ReentrantLock 核心用法

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 的关键。

相关推荐
GreenTea7 小时前
一文搞懂Harness Engineering与Meta-Harness
前端·人工智能·后端
我是大猴子9 小时前
Spring代理类为何依赖注入失效?
java·后端·spring
码事漫谈9 小时前
手把手带你部署本地模型,让你Token自由(小白专属)
前端·后端
码农BookSea10 小时前
ReAct:让大模型学会边想边做
后端·ai编程
码农BookSea10 小时前
10分钟掌握 JSON-RPC 协议,面试加分、设计不踩坑
后端
凤年徐11 小时前
C++手撕红黑树:从0到200行,拿下STL map底层核心
c++·后端·算法
IT_陈寒11 小时前
Python的列表推导式里藏了个坑,差点让我加班到凌晨
前端·人工智能·后端
卷无止境12 小时前
podman与docker的区别和生产环境最佳实践
后端
程途知微12 小时前
ConcurrentHashMap线程安全实现原理全解析
java·后端
Mars酱12 小时前
1分钟编写贪吃蛇 | JSnake贪吃蛇单机版
java·后端·开源