You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java单题限时测验开发遇阻:如何实现超时自动切换题目?

解决限时测验题自动切换的实用方案

我懂你现在的困扰——想做个每道题20秒限时的测验程序,超时自动切下一题,试了java.util.Timer、ExecutorService还有循环计时都没搞定,对吧?咱们来梳理几个靠谱的技术方向,还有要避开的坑:

1. 用ScheduledExecutorService实现精准计时与任务切换

这比Timer靠谱多了,Timer是单线程,一个任务延迟会连累其他,而ScheduledExecutorService是线程池实现,稳定性拉满。核心思路是:每展示一道题,就调度一个20秒后执行的切换任务;如果用户在20秒内提交答案,就取消这个调度任务,避免重复切换。

给你个极简的代码框架参考:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class QuizRunner {
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private ScheduledFuture<?> currentTimeoutTask;
    private int currentQuestionIdx = 0;
    private final String[] questions = {"Java的父类是?", "线程池核心接口是?", "CountDownLatch作用是?"};

    public void launchNextQuestion() {
        // 先取消上一题的超时任务(防止还没到时间就切换的情况)
        if (currentTimeoutTask != null && !currentTimeoutTask.isDone()) {
            currentTimeoutTask.cancel(false);
        }

        // 判断是否所有题目都做完了
        if (currentQuestionIdx >= questions.length) {
            scheduler.shutdown();
            System.out.println("测验结束!");
            return;
        }

        // 展示当前题目
        System.out.printf("第%d题:%s(限时20秒)\n", currentQuestionIdx + 1, questions[currentQuestionIdx]);

        // 调度20秒后的自动切换任务
        currentTimeoutTask = scheduler.schedule(() -> {
            System.out.println("时间到!自动切换下一题");
            currentQuestionIdx++;
            launchNextQuestion();
        }, 20, TimeUnit.SECONDS);

        // 单独开线程处理用户输入,避免阻塞计时逻辑
        new Thread(() -> {
            var scanner = new java.util.Scanner(System.in);
            String userAnswer = scanner.nextLine();
            // 这里可以加答案校验逻辑
            System.out.printf("你提交的答案:%s\n", userAnswer);
            // 用户提交后切换下一题
            currentQuestionIdx++;
            launchNextQuestion();
        }).start();
    }

    public static void main(String[] args) {
        new QuizRunner().launchNextQuestion();
    }
}

重点提醒:用户输入必须放在单独线程,不然主线程被Scanner.nextLine()阻塞,计时任务就算在后台跑,也没法及时响应用户提交的切换操作。

2. 用CountDownLatch实现超时等待逻辑

这个思路更直观:每道题启动两个线程,一个等用户输入,一个负责计时20秒,用CountDownLatch来同步两者的状态——要么用户输入完成触发切换,要么超时自动触发切换。

代码片段参考:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class SingleQuestionHandler {
    public void runSingleQuestion(String question) {
        CountDownLatch latch = new CountDownLatch(1);
        System.out.println("题目:" + question + "(20秒内作答)");

        // 用户输入线程
        new Thread(() -> {
            var scanner = new java.util.Scanner(System.in);
            String answer = scanner.nextLine();
            System.out.println("你的答案:" + answer);
            latch.countDown(); // 用户完成输入,触发 latch 计数减1
        }).start();

        // 等待20秒,要么用户输入完成,要么超时
        try {
            boolean answeredOnTime = latch.await(20, TimeUnit.SECONDS);
            if (!answeredOnTime) {
                System.out.println("超时未作答!");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        // 这里执行切换下一题的逻辑
        System.out.println("进入下一题...");
    }
}

这个方法的好处是不用手动管理任务取消,靠await的超时机制就能判断是否超时,逻辑清晰易懂。

3. 你之前踩过的坑,要注意避开

  • 别用while循环+System.currentTimeMillis()轮询:这种方式会空耗CPU,而且如果主线程被用户输入阻塞,轮询根本跑不起来,完全不可靠。
  • 放弃java.util.Timer:它是单线程实现,只要有一个任务抛出异常,整个Timer就会崩溃,而且任务之间会互相影响,稳定性远不如ScheduledExecutorService。
  • 绝对不能让用户输入阻塞主线程:用户输入是阻塞操作,必须放在单独线程,否则计时逻辑会彻底失效。

该查哪些资料?

  • 优先看Java并发包java.util.concurrent的官方文档:重点啃ScheduledExecutorService、CountDownLatch、ScheduledFuture这几个类的用法,这是Java处理定时任务和并发同步的核心工具。
  • 补一下Java线程基础:理解线程的启动、阻塞、中断机制,特别是如何避免主线程被IO操作(比如用户输入)阻塞。
  • 学习异步任务调度:搞清楚Java中如何实现异步任务的调度与取消,这是实现超时切换功能的核心。

内容的提问来源于stack exchange,提问作者dave

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:50:55