能否用volatile避免多线程重复执行方法?附代码咨询
你的volatile方案确实存在竞态问题,这样改就能实现需求
你说得完全没错——你当前的volatile实现一定会出现多个线程同时进入test()方法执行的情况,volatile没办法单独解决这个问题。
为什么volatile不行?
volatile只能保证两个核心特性:
- 变量的可见性:一个线程修改了volatile变量,其他线程能立刻看到最新值
- 禁止指令重排:避免代码执行顺序被编译器优化打乱
但你代码里的if (test) return; test = true;是两个独立的操作,这两步组合起来不是原子的。举个典型的竞态场景:
- 线程A检查
test是false,还没来得及把test设为true - 线程B同时也检查
test是false,顺利进入方法 - 接下来两个线程都会把
test设为true,然后执行整个方法体
这就完全不符合你“其他线程正在执行时跳过”的需求了。
可行的解决方案
针对你的场景(定期执行+手动触发,无需排队,直接跳过),推荐两种简洁靠谱的方案:
方案1:用AtomicBoolean(轻量首选)
AtomicBoolean的compareAndSet方法是原子操作,能帮你把“检查+设置”合并成一步,保证同时只有一个线程能获取执行权:
import java.util.concurrent.atomic.AtomicBoolean; private static final AtomicBoolean isMethodRunning = new AtomicBoolean(false); public void test() { // 原子性检查:如果当前是false,就设为true;返回true表示成功抢到执行权 if (!isMethodRunning.compareAndSet(false, true)) { return; // 已经有线程在执行,直接跳过 } try { System.out.println("test() started in Thread with ID " + Thread.currentThread().getId()); Thread.sleep(10000); System.out.println("test() finished in Thread with ID " + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 无论执行成功还是抛出异常,都要把状态改回去 isMethodRunning.set(false); } }
方案2:用synchronized(传统稳妥方案)
如果不想引入原子类,用synchronized同步块也能解决,它能保证同一时间只有一个线程进入同步代码段,从而避免竞态:
private static final Object lockObj = new Object(); private static boolean isMethodRunning = false; public void test() { synchronized (lockObj) { if (isMethodRunning) { return; } isMethodRunning = true; } try { System.out.println("test() started in Thread with ID " + Thread.currentThread().getId()); Thread.sleep(10000); System.out.println("test() finished in Thread with ID " + Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } finally { synchronized (lockObj) { isMethodRunning = false; } } }
这里同步块只包裹了状态检查和设置的逻辑,方法体的执行不需要同步,性能比直接给方法加synchronized更好。
总结
volatile没法解决你的问题,因为它管不了“检查-设置”这类复合操作的原子性。上面两种方案都能完美满足你的需求:当已有线程在执行test()时,后续触发的线程会直接跳过,不会排队等待。
内容的提问来源于stack exchange,提问作者ka3ak
相关产品推荐
相关产品推荐

