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

相关推荐
用户30750093037931 天前
go Eino使用ADK开发agent
后端
唐叔在学习1 天前
Python自动化指令进阶:UAC提权
后端·python
Assby1 天前
Windows 在 PostgreSQL 上安装 vector 扩展
后端
12344521 天前
【面试复盘】有了equals为什么还要hashcode
java·后端
小周在成长1 天前
MyBatis 分页插件PageHelper
后端
Paladin_z1 天前
Easy Query中间件的使用
后端
牛奔1 天前
Go语言中结构体转Map优雅实现
开发语言·后端·macos·golang·xcode
掘金码甲哥1 天前
我不允许谁还分不清这三种watch机制的区别
后端
张心独酌1 天前
Rust新手练习案例库- rust-learning-example
开发语言·后端·rust
码事漫谈1 天前
一文读懂“本体论”这个时髦词
后端