如何用ReentrantLock实现数组每秒清理且不阻塞非清理线程?
用ReentrantLock实现数组定时清理与并发添加的方案
嘿,针对你想用ReentrantLock实现数组每秒自动清理,清理时阻塞所有修改操作、非清理时修改无需等待的需求,我整理了一个贴合场景的解决方案,连Netty环境的适配细节也给你考虑到了:
核心思路拆解
- 锁的使用策略:
- 对于
add操作,用ReentrantLock的非阻塞获取锁(tryLock()):如果当前没有在清理,直接拿到锁修改数组;如果清理线程正在占用锁,直接跳过(或者按业务需求处理),完全不阻塞其他正常请求。 - 清理线程则独占锁(
lock()),确保清理期间绝对没有其他线程修改数组,保证数据一致性。
- 对于
- 定时任务选型:用定时线程池(或者Netty自带的EventLoop定时API)每秒触发一次清理,和Netty的EventLoop线程模型兼容。
完整代码示例
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; public class ArrayCleaner { private final List<Object> array = new ArrayList<>(); private final ReentrantLock lock = new ReentrantLock(); private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); public ArrayCleaner() { // 初始化定时任务:每秒执行一次清理 scheduler.scheduleAtFixedRate(this::cleanArray, 1, 1, TimeUnit.SECONDS); } public void addToArray(Object a) { // 尝试获取锁,拿到就修改数组,拿不到直接跳过(可根据业务调整) if (lock.tryLock()) { try { array.add(a); } finally { // 必须在finally里释放锁,防止异常导致锁泄漏 lock.unlock(); } } else { // 这里示例是直接打印提示,你可以改成缓存到临时队列、重试等逻辑 System.out.println("数组正在清理,本次添加请求跳过"); } } private void cleanArray() { // 独占锁,确保清理期间没有其他线程修改数组 lock.lock(); try { System.out.printf("开始清理数组,当前元素数:%d%n", array.size()); array.clear(); System.out.println("数组清理完成"); } finally { lock.unlock(); } } // 关闭定时任务,避免内存泄漏 public void shutdown() { scheduler.shutdown(); try { if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } } }
Netty场景适配说明
因为你提到Netty EventLoop会调用网络处理器,为了契合Netty的线程模型,你可以把定时任务替换成Netty EventLoop的API,不用额外创建线程池:
// 假设你已经有可用的EventLoop实例(比如从EventLoopGroup中获取) EventLoop eventLoop = ...; // 用Netty的定时任务触发清理 eventLoop.scheduleAtFixedRate(this::cleanArray, 1, 1, TimeUnit.SECONDS);
这样清理任务会在Netty的IO线程中执行,避免线程上下文切换的开销。
关键细节提醒
tryLock()的灵活用法:如果你的业务不允许丢弃添加请求,可以用tryLock(100, TimeUnit.MILLISECONDS)设置一个短暂等待时间,或者把无法添加的元素放到一个临时队列,等清理完成后再批量写入数组。- 锁的绝对释放:无论
add还是clean操作,一定要在finally块里释放锁,哪怕出现异常也不会导致死锁。 - 资源回收:定时任务的线程池(或Netty EventLoop)要在程序退出时正确关闭,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Lost2
相关产品推荐
相关产品推荐

