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 中给出代码调用示例及注意事项;
- 对比
synchronized
与ReentrantLock
的使用差异。
三、相关技术详细介绍
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
启动多个线程; - 使用
CountDownLatch
或CyclicBarrier
协调线程启动同步测试; - 记录锁获取次数与失败次数进行统计。
四、实现思路详细介绍
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 notEmpty
、notFull
并在方法中配合使用。
公平与非公平锁对比
- 在测试中构造两种
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()
等方法便于在运行时诊断锁状态。
项目常见问题及解答
为何要在 finally
中 unlock()
?
tryLock
获不到锁后还能重试吗?
公平锁性能更差吗?
是的,公平锁会增加上下文切换成本,一般在需要严格顺序时使用,否则推荐默认非公平锁。lockInterruptibly
如何正确处理中断?
throws InterruptedException
,在捕获后可执行清理逻辑或直接结束任务。
如何监控生产环境中的锁竞争?
利用lock.getQueueLength()
和日志定期采集,或结合 APM 工具监控线程等待情况。
扩展方向与性能优化
Condition 多队列
使用多个Condition
实现更精细的等待/唤醒控制,例如生产者—消费者的 notFull
/ notEmpty
。
锁分段
对大数据结构进行分段加锁(类似 ConcurrentHashMap),降低锁粒度提升并发度。公平性调优
在高并发场景下考虑非公平锁与超时重试结合,避免严格公平带来的吞吐下降。锁剥离
当只有读操作时,可使用ReadwriteLock
切换到无阻塞读锁,提高并发读性能。
可视化诊断
集成到监控平台或定制 Web 界面,实时展示锁争用、队列长度和线程等待图。以上就是Java使用重入锁实现线程同步的示例代码的详细内容,更多关于Java重入锁线程同步的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论