循环迭代前的非阻塞延迟失效问题求助
我明白你现在的困扰——想用非阻塞的方式在循环每次迭代前等一会儿,结果用Timer的时候所有任务一下子都启动了,完全没达到预期的延迟效果对吧?
先说说你遇到的核心问题
在for循环里创建Timer并调度任务时,循环会在极短时间内跑完所有迭代,把所有Timer任务一股脑塞进调度队列。如果你的延迟时间是固定值(比如每次等3秒),这些任务会几乎同时触发——因为循环执行的时间远小于你的延迟时间,所以看起来所有myProcessfor20Seconds()都要等循环结束后才会开始执行,完全没实现“每次迭代前等待”的逻辑。
而且Timer本身也有局限性:它是单线程的,一旦某个任务抛出未捕获的异常,整个Timer都会停止工作,可靠性不如Java并发包提供的工具。
给你两个靠谱的解决方案
方案1:顺序执行(等待→执行→等待→执行,非阻塞)
如果你希望每次必须等前一个myProcessfor20Seconds()执行完,再等待一段时间启动下一个,用ScheduledExecutorService配合递归调度是最好的选择,全程不会阻塞主线程:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class DelayedProcessDemo { // 自定义参数:循环总次数、每次迭代前等待的秒数 private static final int TOTAL_LOOPS = 5; private static final long WAIT_BEFORE_ITERATION = 3; // 单线程调度器,保证任务顺序执行 private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private static final AtomicInteger loopCounter = new AtomicInteger(0); public static void main(String[] args) { // 启动第一个任务 scheduleNextProcess(); // 这里可以放心执行其他代码,不会被阻塞 System.out.println("主线程继续处理其他任务..."); } private static void scheduleNextProcess() { int currentLoop = loopCounter.getAndIncrement(); if (currentLoop >= TOTAL_LOOPS) { scheduler.shutdown(); // 所有任务完成后关闭调度器,避免内存泄漏 return; } // 先等待指定时间,再执行耗时任务;任务完成后自动调度下一次 scheduler.schedule(() -> { try { myProcessfor20Seconds(currentLoop); } finally { scheduleNextProcess(); } }, WAIT_BEFORE_ITERATION, TimeUnit.SECONDS); } private static void myProcessfor20Seconds(int loopNum) { System.out.println("开始执行第" + (loopNum + 1) + "次耗时任务"); try { Thread.sleep(20000); // 模拟20秒的耗时操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("第" + (loopNum + 1) + "次任务被中断"); } System.out.println("第" + (loopNum + 1) + "次耗时任务执行完成"); } }
这个方案的优势:
- 用
AtomicInteger做线程安全的计数,避免多线程下的计数错误 - 单线程调度器保证任务按顺序执行,不会同时运行多个
myProcessfor20Seconds() - 全程非阻塞,主线程可以正常处理其他逻辑
方案2:并行执行(固定间隔启动任务,不管前一个是否完成)
如果你允许同时运行多个myProcessfor20Seconds(),只是希望每次启动任务前间隔固定时间,那可以直接在循环里给每个任务设置递增的延迟:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ParallelDelayedProcessDemo { private static final int TOTAL_LOOPS = 5; private static final long INTERVAL = 3; public static void main(String[] args) { // 根据需求调整线程池大小,避免同时运行太多任务导致资源耗尽 ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3); for (int i = 0; i < TOTAL_LOOPS; i++) { int loopNum = i; // 第1次等3秒,第2次等6秒...以此类推,实现固定间隔启动 scheduler.schedule(() -> { myProcessfor20Seconds(loopNum); }, INTERVAL * (i + 1), TimeUnit.SECONDS); } // 主线程继续执行其他操作 System.out.println("主线程继续处理其他任务..."); } private static void myProcessfor20Seconds(int loopNum) { System.out.println("并行启动第" + (loopNum + 1) + "次耗时任务"); try { Thread.sleep(20000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("第" + (loopNum + 1) + "次并行任务被中断"); } System.out.println("第" + (loopNum + 1) + "次并行任务执行完成"); } }
最后提醒
如果是在Android环境下使用,记得不要在主线程中执行myProcessfor20Seconds()(上面的方案已经用调度器放在后台线程了,没问题),另外在页面销毁或任务不再需要时,一定要调用scheduler.shutdown()或scheduler.shutdownNow(),避免内存泄漏。
内容的提问来源于stack exchange,提问作者User52 1
相关产品推荐
相关产品推荐

