Java多线程:如何用wait()和notify()获取单线程结果并终止另一线程
解决两个线程抢先返回结果并终止另一个的问题
你的需求本质是获取第一个完成的异步任务结果,同时终止未完成的任务,手动用wait()/notify()容易踩坑,直接用Java并发包提供的工具类就能完美解决,不用自己造轮子。
核心方案:ExecutorService.invokeAny()
ExecutorService.invokeAny()会提交一组Callable任务,返回第一个成功完成的任务结果,并且自动中断(取消)其他还在运行的任务,完全匹配你的需求。
不过要针对你的两个特殊任务做适配:
1. 控制台输入任务(Scanner)
把Scanner的输入逻辑包装成Callable:
import java.util.Scanner; import java.util.concurrent.Callable; public class ScannerInputTask implements Callable<String> { @Override public String call() throws Exception { Scanner scanner = new Scanner(System.in); try { System.out.println("控制台输入:回车确认"); return scanner.nextLine(); } finally { scanner.close(); } } }
2. 按键输入任务(KeyListener)
KeyListener依赖AWT事件线程,需要用CountDownLatch来等待按键触发:
import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import javax.swing.JFrame; import javax.swing.JTextField; public class KeyInputTask implements Callable<String> { private final CountDownLatch latch = new CountDownLatch(1); private String inputContent; @Override public String call() throws Exception { JFrame inputFrame = new JFrame("按键输入窗口"); JTextField inputBox = new JTextField(); inputFrame.add(inputBox); inputFrame.setSize(300, 100); inputFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); inputFrame.setVisible(true); inputBox.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { inputContent = inputBox.getText(); latch.countDown(); inputFrame.dispose(); } } }); // 阻塞等待按键触发 latch.await(); return inputContent; } }
3. 主线程整合逻辑
用线程池执行任务,调用invokeAny()获取第一个结果:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(2); List<Callable<String>> taskList = new ArrayList<>(); taskList.add(new ScannerInputTask()); taskList.add(new KeyInputTask()); try { // 拿到第一个完成的结果,自动中断另一个任务 String firstResult = threadPool.invokeAny(taskList); System.out.println("最先获取的输入:" + firstResult); } catch (Exception e) { e.printStackTrace(); } finally { // 强制关闭线程池,确保所有线程终止 threadPool.shutdownNow(); } } }
为什么不用手动wait/notify?
你之前的写法有几个硬伤:
Thread.currentThread().wait()必须在同步块中调用,且锁对象必须是当前线程对象,这种用法本身就不符合多线程规范- 手动终止线程(比如你说的“delete thread”)没有安全的方式,只能通过中断信号,而
invokeAny()已经帮你处理了中断逻辑 - 手动处理线程完成的通知容易遗漏场景,比如线程抛出异常时无法触发notify
关键注意点
- Scanner的
nextLine()会响应中断,收到中断信号后直接抛出异常,任务终止 - KeyInputTask中的
latch.await()会捕获中断信号,抛出异常后任务终止,同时窗口会被关闭
内容的提问来源于stack exchange,提问作者StupidQuestion
相关产品推荐
相关产品推荐

