Java线程延迟执行代码时调用timerStart无效问题求助
问题原因与解决方案
问题原因
- 线程可见性缺失:
lastEdit变量没有标记为volatile,执行run()方法的线程会将变量缓存到自身工作内存中,其他线程修改lastEdit后,该线程无法及时感知主存中的最新值,导致判断逻辑一直使用旧的缓存值,看起来修改未生效。 - static方法调用误区:
timerStart是static方法,你通过anotherThread.timerStart(12000)调用时,本质是调用类的static方法,和anotherThread实例无关。如果lastEdit是static变量,这部分本身无问题,但核心矛盾还是线程可见性。 - 忙循环的JIT优化问题:
while(true)的空循环会触发JIT编译器优化,可能将lastEdit的读取操作直接优化为从寄存器读取,彻底忽略主存中的更新。
解决方案
方案1:修复线程可见性(最直接)
给lastEdit添加volatile修饰符,强制线程每次读取都从主存获取最新值,写入时立即同步到主存:
private static volatile long lastEdit; // 增加volatile修饰 public void run() { System.out.println("it is running"); while (true) { if (System.currentTimeMillis() > lastEdit) { System.out.println("DELETE"); timerStart(12000); } // 加入微小sleep,减少CPU空转 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } public static void timerStart(int time) { lastEdit = System.currentTimeMillis() + time; }
如果存在多线程同时调用timerStart的场景,lastEdit = ...不是原子操作(读取、计算、写入三步),此时需要用AtomicLong保证原子性:
private static final AtomicLong lastEdit = new AtomicLong(); public static void timerStart(int time) { lastEdit.set(System.currentTimeMillis() + time); } // run方法中的判断需修改为: if (System.currentTimeMillis() > lastEdit.get())
方案2:使用更高效的定时方式(推荐)
放弃忙循环,用Java自带的ScheduledExecutorService实现延迟执行,既避免CPU空转,也天然解决线程同步问题:
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private Future<?> currentTask; public void start() { System.out.println("it is running"); scheduleDelete(12000); } private void scheduleDelete(long delay) { // 取消之前的未完成任务 if (currentTask != null && !currentTask.isDone()) { currentTask.cancel(false); } currentTask = scheduler.schedule(() -> { System.out.println("DELETE"); scheduleDelete(12000); // 执行完成后调度下一次 }, delay, TimeUnit.MILLISECONDS); } // 外部调用此方法重置延迟 public void resetTimer(int time) { scheduleDelete(time); } // 程序结束时关闭线程池 public void shutdown() { scheduler.shutdown(); }
这种方式无需手动处理变量同步,由线程池负责调度,可靠性和效率更高。
内容的提问来源于stack exchange,提问作者Cecil
相关产品推荐
相关产品推荐

