Java使用重入锁实现线程同步的示例代码

 更新时间:2025年07月07日 09:28:36   作者:Katie。  
在多线程环境中,为保证对共享资源的安全访问,常用的同步手段是 synchronized 关键字,但 synchronized 存在局限,本项目旨在通过示例演示如何使用 ReentrantLock 实现线程同步,替代 synchronized,并展示其高级功能,需要的朋友可以参考下

一、项目背景详细介绍

在多线程环境中,为保证对共享资源的安全访问,常用的同步手段是 synchronized 关键字。但 synchronized 存在以下局限:

  • 灵活性较低:不能尝试超时获取锁,也无法中断获取锁的线程;
  • 可见性:无法查看当前锁是否被占用或等待队列情况;
  • 公平性控制:无法直接控制锁的公平或非公平策略。

Java 5 引入了更强大的 java.util.concurrent.locks 包,其中的 ReentrantLock(可重入锁)在功能和性能上均优于内置锁。它提供:

  • 尝试获取锁tryLock()tryLock(timeout, unit)
  • 可中断的锁获取lockInterruptibly()
  • 公平锁:通过构造函数选择公平或非公平策略
  • 监视器查询getHoldCount()isLocked()getQueueLength() 等方法

本项目旨在通过示例演示如何使用 ReentrantLock 实现线程同步,替代 synchronized,并展示其高级功能,如超时尝试获取锁、公平策略和中断响应。

二、项目需求详细介绍

基本互斥访问

  • 使用 ReentrantLock 替代 synchronized,在多个线程间安全地更新同一共享变量或数据结构;
  • 提供示例:多线程对同一计数器或共享列表进行增删操作。

超时获取锁

  • 演示 tryLock(long timeout, TimeUnit unit) 用法,当锁长时间被占用时抛出或走备用逻辑;

可中断锁获取

  • 演示 lockInterruptibly(),在等待锁期间响应中断,避免因锁阻塞导致的无法取消;

公平锁与非公平锁

  • 对比默认(非公平)锁和通过 new ReentrantLock(true) 创建的公平锁在高并发场景下的性能及线程调度差异;

锁状态监控

  • 使用 getHoldCount()getQueueLength()hasQueuedThreads() 等方法,实时查询锁的占用与等待情况,打印日志监控;

示例应用

  • 实现一个带超时和中断功能的共享资源访问类 SharedResource
  • 编写多线程测试,模拟高并发下的锁获取、超时回退和中断场景;

配置可控

  • 通过构造参数或配置文件动态切换公平性、超时阈值等;

文档与示例

  • 在 README 中给出代码调用示例及注意事项;
  • 对比 synchronizedReentrantLock 的使用差异。

三、相关技术详细介绍

java.util.concurrent.locks.ReentrantLock

  • 基本方法lock()unlock()lockInterruptibly()tryLock()tryLock(timeout, unit)
  • 构造参数new ReentrantLock()(非公平锁)、new ReentrantLock(true)(公平锁);

Condition 接口

  • 通过 lock.newCondition() 创建条件变量,替代 wait/notify,支持多条件队列;
  • 方法:await()signal()signalAll()

锁监控与诊断

  • getHoldCount():返回当前线程重入次数;
  • isLocked():锁是否被任意线程占用;
  • hasQueuedThreads()getQueueLength():等待锁的线程信息;

中断与超时

  • lockInterruptibly() 在锁等待时可响应中断;
  • tryLock(timeout, unit) 在指定时长内等待,超时后返回 false

多线程测试

  • 使用 ExecutorService 启动多个线程;
  • 使用 CountDownLatchCyclicBarrier 协调线程启动同步测试;
  • 记录锁获取次数与失败次数进行统计。

四、实现思路详细介绍

SharedResource 类设计

  • 内部包含 private final ReentrantLock lock; 和可选的 Condition
  • 提供方法:
void safeIncrement() { lock.lock(); try { /* 更新共享计数 */ } finally { lock.unlock(); } }
boolean trySafeIncrement(long timeout, TimeUnit unit) { if (lock.tryLock(timeout, unit)) { try { ... } finally { lock.unlock(); } } else { /* 超时逻辑 */ } }
void interruptibleAccess() throws InterruptedException { lock.lockInterruptibly(); try { ... } finally { lock.unlock(); } }
  • 若需要多个条件,可创建 Condition notEmptynotFull 并在方法中配合使用。

公平与非公平锁对比

  • 在测试中构造两种 SharedResource,公平锁与非公平锁,分别运行相同并发测试,比较吞吐量与线程饥饿情况。

监控锁状态

  • 在方法中或监控线程里定期调用 lock.getQueueLength()lock.hasQueuedThreads() 并打印,观察等待线程数;
  • 结合 lock.getHoldCount() 了解重入深度。

多线程测试

  • 使用 ExecutorService 和多个工作线程不断调用不同模式的方法;
  • 使用 CountDownLatch 保证开始同步,使用 AtomicInteger 统计成功与超时/中断次数;

文档示例

  • 在 README 中说明各模式使用场景和注意事项,例如必须在 finally 块中 unlock(),避免死锁。
/*
 * =====================================================
 * File: SharedResource.java
 * 共享资源类,使用 ReentrantLock 实现多种同步策略
 * =====================================================
 */
package com.example.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SharedResource {
    private int counter = 0;
    private final ReentrantLock lock;
    private final Condition notZero;

    /**
     * 构造函数:可指定是否公平锁
     */
    public SharedResource(boolean fair) {
        this.lock = new ReentrantLock(fair);
        this.notZero = lock.newCondition();
    }

    /**
     * 基本互斥:安全地递增 counter
     */
    public void safeIncrement() {
        lock.lock();
        try {
            counter++;
            System.out.printf("%s incremented to %d%n",
                Thread.currentThread().getName(), counter);
            notZero.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 带超时尝试获取锁的递增
     */
    public boolean trySafeIncrement(long timeout, TimeUnit unit) {
        boolean acquired = false;
        try {
            acquired = lock.tryLock(timeout, unit);
            if (acquired) {
                counter++;
                System.out.printf("%s timed increment to %d%n",
                    Thread.currentThread().getName(), counter);
                notZero.signalAll();
                return true;
            } else {
                System.out.printf("%s failed to acquire lock in %d %s%n",
                    Thread.currentThread().getName(), timeout, unit);
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.printf("%s interrupted while waiting%n",
                Thread.currentThread().getName());
            return false;
        } finally {
            if (acquired) lock.unlock();
        }
    }

    /**
     * 可中断地获取锁并等待 counter > 0 后消费
     */
    public int interruptibleConsume() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (counter == 0) {
                System.out.printf("%s waiting for counter > 0%n",
                    Thread.currentThread().getName());
                notZero.await();
            }
            counter--;
            System.out.printf("%s consumed to %d%n",
                Thread.currentThread().getName(), counter);
            return counter;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 监控方法:打印当前锁状态
     */
    public void printLockStatus() {
        System.out.printf("Lock held by thread: %s, holdCount=%d, queuedThreads=%d%n",
            lock.isLocked() && lock.isHeldByCurrentThread()
                ? Thread.currentThread().getName()
                : "other",
            lock.getHoldCount(),
            lock.getQueueLength());
    }
}


/*
 * =====================================================
 * File: LockDemo.java
 * 演示:多线程调用 SharedResource 不同方法
 * =====================================================
 */
package com.example.lock;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class LockDemo {
    public static void main(String[] args) throws InterruptedException {
        SharedResource fairResource = new SharedResource(true);
        SharedResource unfairResource = new SharedResource(false);

        ExecutorService exec = Executors.newFixedThreadPool(6);
        CountDownLatch startLatch = new CountDownLatch(1);
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failCount = new AtomicInteger(0);

        // 创建 2 个常规增量任务
        for (int i = 0; i < 2; i++) {
            exec.submit(() -> {
                await(startLatch);
                fairResource.safeIncrement();
            });
        }

        // 创建 2 个带超时尝试锁任务
        for (int i = 0; i < 2; i++) {
            exec.submit(() -> {
                await(startLatch);
                if (fairResource.trySafeIncrement(500, TimeUnit.MILLISECONDS)) {
                    successCount.incrementAndGet();
                } else {
                    failCount.incrementAndGet();
                }
            });
        }

        // 创建 2 个可中断消费任务
        for (int i = 0; i < 2; i++) {
            exec.submit(() -> {
                await(startLatch);
                try {
                    unfairResource.interruptibleConsume();
                } catch (InterruptedException e) {
                    System.out.printf("%s interrupted%n",
                        Thread.currentThread().getName());
                }
            });
        }

        // 启动所有任务
        startLatch.countDown();

        // 等待一段时间后中断消费任务
        Thread.sleep(1000);
        exec.shutdownNow();
        exec.awaitTermination(5, TimeUnit.SECONDS);

        System.out.printf("TryLock successes: %d, failures: %d%n",
            successCount.get(), failCount.get());
    }

    private static void await(CountDownLatch latch) {
        try {
            latch.await();
        } catch (InterruptedException ignored) {}
    }
}

代码详细解读

SharedResource

  • safeIncrement():使用 lock.lock()unlock() 实现基本互斥,并在 notZero 条件上唤醒等待的消费者。
  • trySafeIncrement(timeout, unit):使用 tryLock(timeout, unit) 带超时尝试获取锁,超时后返回失败逻辑。
  • interruptibleConsume():使用 lock.lockInterruptibly(),在等待中可响应中断,配合 notZero.await() 等待条件。
  • printLockStatus():演示查询锁状态的方法,包括持有计数和等待队列长度。

LockDemo

使用 ExecutorService 启动 6 个线程:

  • 2 个调用 safeIncrement()
  • 2 个调用 trySafeIncrement(500ms),统计成功与失败次数;
  • 2 个调用 interruptibleConsume(),并在主线程中断它们,演示可中断锁获取;
  • 使用 CountDownLatch 保证所有线程同时开始。
  • 程序运行 1 秒后调用 shutdownNow() 中断消费任务,并打印 tryLock 的统计结果。

项目详细总结

本示例通过 ReentrantLock 展示了:

  • 基本互斥:与 synchronized 类似,但可以更灵活地控制锁释放时机。
  • 超时获取锁tryLock(timeout, unit) 避免长时间阻塞,方便实现备用逻辑。
  • 可中断锁lockInterruptibly() 在等待锁时响应中断,提高了取消能力。
  • 公平与非公平:可通过构造函数选择公平策略,避免线程饥饿。
  • 锁监控getHoldCount()getQueueLength() 等方法便于在运行时诊断锁状态。

项目常见问题及解答

为何要在 finallyunlock()
避免在执行过程中抛出异常导致锁未释放,进而引发死锁。

tryLock 获不到锁后还能重试吗?
可以在代码中判断失败后循环调用,或结合退避机制重试。

公平锁性能更差吗?
是的,公平锁会增加上下文切换成本,一般在需要严格顺序时使用,否则推荐默认非公平锁。

lockInterruptibly 如何正确处理中断?
调用方法需声明 throws InterruptedException,在捕获后可执行清理逻辑或直接结束任务。

如何监控生产环境中的锁竞争?
利用 lock.getQueueLength() 和日志定期采集,或结合 APM 工具监控线程等待情况。

扩展方向与性能优化

Condition 多队列
使用多个 Condition 实现更精细的等待/唤醒控制,例如生产者—消费者的 notFull / notEmpty

锁分段
对大数据结构进行分段加锁(类似 ConcurrentHashMap),降低锁粒度提升并发度。

公平性调优
在高并发场景下考虑非公平锁与超时重试结合,避免严格公平带来的吞吐下降。

锁剥离
当只有读操作时,可使用 ReadWriteLock 切换到无阻塞读锁,提高并发读性能。

可视化诊断
集成到监控平台或定制 Web 界面,实时展示锁争用、队列长度和线程等待图。

以上就是Java使用重入锁实现线程同步的示例代码的详细内容,更多关于Java重入锁线程同步的资料请关注脚本之家其它相关文章!

相关文章

  • 如何在Java中调用python文件执行详解

    如何在Java中调用python文件执行详解

    丰富的第三方库使得python非常适合用于进行数据分析,最近在项目中就涉及到java调用python实现的算法,下面这篇文章主要给大家介绍了关于如何在Java中调用python文件执行的相关资料,需要的朋友可以参考下
    2022-05-05
  • SpringBoot中使用Knife4j生成接口文档的示例详解

    SpringBoot中使用Knife4j生成接口文档的示例详解

    Knife4j 是一个基于 Swagger 的增强 UI 实现,主要用于为 Spring Boot 应用程序生成 API 接口文档,本文将详细介绍如何在 Spring Boot 中集成 Knife4j,并通过不同注解来生成清晰的接口文档,需要的可以参考一下
    2025-06-06
  • Java动态线程池插件dynamic-tp集成zookeeper

    Java动态线程池插件dynamic-tp集成zookeeper

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等
    2023-03-03
  • Java中computeIfAbsent的功能和语法小结

    Java中computeIfAbsent的功能和语法小结

    Java8的computeIfAbsent方法简化了从Map中获取值并处理默认值的过程,减少了手动null检查的繁琐,提高了代码的简洁性和可读性,本文就来具体了解一下computeIfAbsent的使用,感兴趣的可以了解一下
    2025-10-10
  • 深入理解Java设计模式之代理模式

    深入理解Java设计模式之代理模式

    这篇文章主要介绍了Java设计模式之代理模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
    2021-11-11
  • 10个Elasticsearch查询的实用技巧分享

    10个Elasticsearch查询的实用技巧分享

    Elasticsearch是一个非常流行的搜索引擎,已经成为了许多企业的首选解决方案。本文将向大家介绍10个实用的Elasticsearch查询技巧,并配上对应的代码示例,希望对大家有所帮助
    2023-04-04
  • MyBatis中防止SQL注入讲解

    MyBatis中防止SQL注入讲解

    这篇文章主要介绍了MyBatis中防止SQL注入,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-12-12
  • Spring Validation接口入参校验示例代码

    Spring Validation接口入参校验示例代码

    Spring Validation是一种用于实现数据校验的框架,它提供了一系列的校验器,针对不同的数据类型可以使用不同的校验器进行校验,下面这篇文章主要给大家介绍了关于Spring Validation接口入参校验的相关资料,需要的朋友可以参考下
    2023-06-06
  • java 实现web项目启动加载properties属性文件

    java 实现web项目启动加载properties属性文件

    这篇文章主要介绍了java 实现web项目启动加载properties属性文件,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • SpringBoot同时集成Mybatis和Mybatis-plus框架

    SpringBoot同时集成Mybatis和Mybatis-plus框架

    在实际开发中,项目里面一般都是Mybatis和Mybatis-Plus公用,但是公用有版本不兼容的问题,本文主要介绍了Spring Boot项目中同时集成Mybatis和Mybatis-plus,具有一档的参考价值,感兴趣的可以了解一下
    2024-12-12

最新评论