开发者

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

目录
  • 一、项目背景详细介绍
  • 二、项目需求详细介绍
  • 三、相关技术详细介绍
  • 四、实现思路详细介绍
    • 代码详细解读
  • 项目详细总结
    • 项目常见问题及解答
      • 扩展方向与性能优化

        一、项目背景详细介绍

        在多线程环境中,为保证对共享资源的安全访问,常用的同步手段是 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
        • 编写多线程测试,模拟高并http://www.devze.com发下的锁获取、超时回退和中断场景;

        配置可控

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

        文档与示例

        • 在 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,公平锁与非公平锁,分别运行相同并发测试,比较吞吐量与线程饥饿情况。

        监控锁状态

        • 在方法中或监控线程里定期调用vqhqNq 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 {
          http://www.devze.com  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 latchjavascript) {
                try {
                    latch.await();
                } catch (InterruptedException ignored) {}
            }
        }
        

        代码详www.devze.com细解读

        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重入锁线程同步的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜