关键在于用blockingqueue解耦输入与处理:输入线程专注读取并存入队列,多个工作线程并行take消费,通过哨兵值协调终止,避免手动同步。

用并发类配合控制台实现数据的持续读取与并行暂存,关键在于分离“输入采集”和“数据处理”两个流程,让它们不互相阻塞。核心不是堆线程,而是合理分工:一个线程专注读取用户输入(阻塞但必须),多个工作线程并行消费暂存的数据。
用 BlockingQueue 做线程安全的中转站
选择 BlockingQueue(如 LinkedBlockingQueue)作为输入与处理之间的缓冲区。它天然支持多生产者-单消费者或多消费者模型,且所有操作线程安全,无需手动加锁。
- 主线程(或专用输入线程)调用
queue.put(line)存入一行输入,若队列满则自动等待 - 多个处理线程调用
queue.take()获取数据,若队列空则自动阻塞,不忙轮询 - 避免自己手写 synchronized + wait/notify,也避开 volatile + while 循环的易错组合
输入线程只做一件事:读、判、存
保持输入逻辑干净。用 Scanner.nextLine() 统一读整行,用字符串判断退出条件(如 "quit"、"exit" 或空行),不掺杂解析或计算。
- 不要在输入线程里调
Integer.parseInt()—— 解析失败会中断输入流,应交给处理线程做,并捕获NumberFormatException - 输入线程检测到终止信号后,可调用
queue.put(POISON_PILL)(如自定义哨兵对象)通知处理线程优雅结束 - 确保
Scanner在程序退出前关闭(通常在输入线程末尾)
多个处理线程并行消费,各自独立出错
启动固定数量的工作线程(如 2–4 个),每个都从队列中持续取数据、解析、执行业务逻辑(如累加、校验、存文件等)。
- 每个线程内部用 try-catch 包裹解析和业务代码,单条数据出错不影响其他线程和队列继续工作
- 处理结果可统一写入共享的
ConcurrentHashMap或AtomicInteger等线程安全容器,避免竞争 - 若需顺序输出(如按输入顺序打印结果),不建议强行串行化处理——可改用带序号的封装对象 + 单独排序线程,或接受最终聚合结果即可
完整结构示意(Java)
主流程三部分清晰分离:
-
输入端:单独线程,
while (true)+nextLine()+queue.put(...) -
处理端:多个
Thread或ExecutorService提交的Runnable,循环queue.take()并处理 -
协调端:用哨兵值或
shutdownNow()配合isShutdown()控制整体生命周期
这种设计下,输入不卡处理,处理不拖输入,真正实现“持续读取”与“并行暂存”的解耦。不复杂但容易忽略。











