Spring Boot异步场景下如何用Lock实现单例执行refresh逻辑?
问题解决:用ReentrantLock实现单次refresh+多线程等待逻辑
ReentrantLock完全可以解决你遇到的问题,实现起来也不复杂,核心是通过锁控制refresh的执行权限,配合可见性标记避免重复执行,具体方案如下:
实现思路
- 用
ReentrantLock保证同一时间只有一个线程能进入refresh的执行判断逻辑; - 用
volatile修饰标记位,确保多线程间能实时感知refresh是否完成; - 双重检查标记位:仅当refresh未执行时,让首个线程执行refresh,后续线程直接跳过。
代码实现
@Service @RequiredArgsConstructor public class TestService { private final TestClient client; private final TestManager manager; private final ReentrantLock refreshLock = new ReentrantLock(); // volatile保证标记位的多线程可见性 private volatile boolean refreshCompleted = false; @Async public void execute() { // 替换成你的具体条件判断逻辑 if (needRefresh()) { try { refreshLock.lock(); // 双重检查:仅当未刷新过时才执行refresh if (!refreshCompleted) { manager.refresh(); refreshCompleted = true; } } finally { // 必须在finally中释放锁,避免异常导致锁泄漏 refreshLock.unlock(); } } // 所有线程最终都会执行send client.send(); } // 可选:如果后续需要重新触发refresh,添加重置标记的方法 public void resetRefreshStatus() { try { refreshLock.lock(); refreshCompleted = false; } finally { refreshLock.unlock(); } } // 替换成你的实际条件判断逻辑 private boolean needRefresh() { // 示例:比如根据配置或业务状态判断是否需要刷新 return true; } }
逻辑说明
- 首个满足条件的线程进入
execute方法后,获取锁并检查标记位,发现未刷新则执行manager.refresh(),完成后将标记设为true,最后释放锁; - 后续满足条件的线程会被锁阻塞,直到首个线程释放锁;拿到锁后检查标记位已为
true,直接跳过refresh逻辑,释放锁后执行client.send(); volatile修饰的标记位确保所有线程能实时看到最新状态,避免线程缓存旧值导致误判;- 锁在
finally块中释放,避免因refresh()抛出异常导致锁无法释放的问题。
额外说明
如果你的业务场景中refresh不是一次性的(比如后续某个时机需要再次触发),可以调用resetRefreshStatus()方法重置标记位,下次满足条件时会重新执行refresh逻辑。
内容的提问来源于stack exchange,提问作者fckNewbee
相关产品推荐
相关产品推荐

