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

Java Timer实现影院订票系统结账倒计时时终端输入无响应问题排查

问题原因分析
  • 线程输入流冲突:Java的Timer启动的TimerTask运行在独立的后台守护线程,你在该线程内新建Scanner读取全局唯一的System.in输入流,而主线程的checkout()方法大概率也在使用Scanner监听用户输入,两个线程抢占输入资源后,你在终端输入的内容会被其中一个线程吃掉,Timer线程的inputObj.next()拿不到输入就会一直阻塞,所以不会执行后续的打印语句。
  • 控制台输出混乱:5分钟超时后Timer线程突然打印过期提示,和主线程的结账流程输出互相穿插,也会导致用户交互逻辑错乱。
  • 额外逻辑错误:你现有代码里不管结账成功失败都对numSeatsAvailable执行+1操作,结账成功占用座位的场景下可用座位数应该减少,这里逻辑写反了。
修复方案

核心原则:所有用户IO操作全部放在主线程执行,Timer后台线程仅负责标记超时状态,不要跨线程操作输入输出流。

步骤1:定义线程共享的超时标记

// 多线程共享的超时标记,加volatile保证多线程下的状态可见性
private volatile boolean isCheckoutTimeout = false;

步骤2:调整expiredCheckout逻辑

该方法改为仅在主线程调用,不用修改内部核心交互逻辑:

private void expiredCheckout() {
    Scanner inputObj = new Scanner(System.in);
    while (true) {
        System.out.println("Your session has expired!");
        System.out.println("If you want to start over, press 1"); 
        System.out.println("If you want to cancel, press q");
        String input = inputObj.next();
        System.out.println("made it here");
        if (input.equals("q"))
            return;
        else if (input.equals("1")) {
            this.runTheatreUI();
            break;
        } else {
            System.out.println("Please enter valid choice!");
        }
    }
}

步骤3:重写startTimer逻辑

Timer后台线程仅负责标记超时,不要处理用户输入:

private void startTimer(ArrayList<Seat> seatsSelected) {
    // 每次启动计时器先重置超时标记
    isCheckoutTimeout = false;
    Timer timer = new Timer();
    timer.schedule(
        new TimerTask() {
            public void run() {
                // 后台线程仅做超时标记和基础提示,不读取输入
                isCheckoutTimeout = true;
                System.out.println("\nYour checkout session has timed out.");
            }
        }
        ,5*60*1000);
    ArrayList<Object> checkoutInfo = this.checkout();
    timer.cancel();

    // 先处理超时场景
    if (isCheckoutTimeout) {
        expiredCheckout();
        // 超时默认释放选中座位
        for (Seat seat : seatsSelected) {
            seat.setStatus(Seat.OPEN);
        }
        this.numSeatsAvailable += seatsSelected.size();
        return;
    }

    // 处理正常结账场景
    if (checkoutInfo.get(0).equals(true)) {
        for (Seat seat : seatsSelected) {
            seat.setStatus(Seat.TAKEN);
        }
        // 结账成功,可用座位数减少
        this.numSeatsAvailable -= seatsSelected.size();
    } else {
        // 用户主动取消,释放座位
        for (Seat seat : seatsSelected) {
            seat.setStatus(Seat.OPEN);
        }
        this.numSeatsAvailable += seatsSelected.size();
    }
}

步骤4:修改checkout方法适配超时逻辑

你需要在原来的checkout方法中加入超时状态轮询,避免一直阻塞等待输入:

// 示例:checkout内的输入读取逻辑调整为非阻塞轮询
private ArrayList<Object> checkout() {
    ArrayList<Object> result = new ArrayList<>();
    Scanner scanner = new Scanner(System.in);
    try {
        while (!isCheckoutTimeout) {
            // 检测是否有用户输入
            if (System.in.available() > 0) {
                String input = scanner.next();
                // 原有结账输入处理逻辑,处理完成后设置result返回
                break;
            }
            // 降低轮询CPU占用
            Thread.sleep(100);
        }
    } catch (IOException | InterruptedException e) {
        // 异常处理逻辑
        result.add(false);
    }
    return result;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:27:03