Java中Scanner与Thread实现并行字符串识别失效问题求助
问题解决:同时支持控制台与JOptionPane并行输入,输入后自动关闭另一个输入源
核心问题分析
你的代码存在三个关键错误,导致并行逻辑失效:
- 线程未真正启动:调用
Thread.run()只是在当前线程执行代码,没有创建新线程。必须用Thread.start()才能启动并行线程。 - Swing线程安全问题:Swing组件(包括JOptionPane)必须在**事件调度线程(EDT)**中创建和操作,直接在自定义线程中调用会引发线程安全问题,导致UI显示异常。
- 缺乏输入互斥终止逻辑:没有实现"一个输入完成后立即关闭另一个输入源"的逻辑,导致两个输入源互相干扰。
修正后的实现代码
以下是完整的可运行解决方案,实现了控制台与弹窗并行输入,任意一个输入完成后自动关闭另一个输入源:
import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class DualInputHandler { private volatile String inputResult = null; private final CountDownLatch latch = new CountDownLatch(1); private JOptionPane currentOptionPane; // 启动控制台输入线程 private void startConsoleInput() { new Thread(() -> { try (Scanner sc = new Scanner(System.in)) { System.out.println("请在控制台输入或在弹窗中选择:"); String consoleInput = sc.nextLine().trim(); if (!consoleInput.isEmpty()) { inputResult = consoleInput; latch.countDown(); // 关闭弹窗 closeOptionPane(); } } }).start(); } // 启动弹窗输入(必须在EDT线程执行) private void startOptionPaneInput() { SwingUtilities.invokeLater(() -> { currentOptionPane = new JOptionPane( "请选择参考项:", JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null, null, "" ); JDialog dialog = currentOptionPane.createDialog("选择名称"); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); // 获取弹窗输入结果 Object result = currentOptionPane.getValue(); if (result != null && result instanceof String && !((String) result).isEmpty()) { inputResult = (String) result; latch.countDown(); } }); } // 关闭弹窗 private void closeOptionPane() { if (currentOptionPane != null) { SwingUtilities.invokeLater(() -> { JDialog dialog = (JDialog) currentOptionPane.getTopLevelAncestor(); if (dialog != null) { dialog.dispose(); } }); } } // 获取最终输入结果 public String getInput() throws InterruptedException { startConsoleInput(); startOptionPaneInput(); // 等待任意一个输入完成,超时时间可自定义 latch.await(5, TimeUnit.MINUTES); return inputResult; } // 测试主方法 public static void main(String[] args) { try { DualInputHandler handler = new DualInputHandler(); String result = handler.getInput(); if (result == null || result.isEmpty() || result.equals("x")) { System.out.println("[注意] 将从数据库加载随机参考项"); } else { System.out.println("你选择的参考项:" + result); } } catch (InterruptedException e) { e.printStackTrace(); } } }
关键实现说明
- 线程启动:使用
Thread.start()真正启动并行线程,控制台输入和弹窗输入分别在独立线程中运行。 - Swing线程安全:通过
SwingUtilities.invokeLater()确保JOptionPane在EDT线程中创建和显示,避免UI异常。 - 输入同步与终止:
- 使用
CountDownLatch等待第一个输入完成,一旦有输入结果就触发latch,结束等待。 - 实现
closeOptionPane()方法,当控制台有输入时,主动关闭弹窗;反之,弹窗输入完成后,控制台线程会自动退出。
- 使用
- 线程安全的结果存储:用
volatile修饰inputResult,确保多线程下的可见性。
集成到你的项目中
你可以将上述DualInputHandler类集成到你的SelectName方法中,替换原有的输入逻辑,例如:
public static String setManualname(LinkedHashMap<Character,LinkedHashMap<String,Namer>> namesList) { try { DualInputHandler handler = new DualInputHandler(); String name = handler.getInput(); // 保留你原有的业务逻辑判断 if (name == null || name.equals("") || name.equals("x")) { System.out.println("[ATTENTION] - a random reference will be loaded from the database."); return ""; } else if (name.equals("auto")) { return "auto"; } else if (name.contains("x_") && name.length() > 2) { deleteName(namesList, name.substring(2), true); return setManualname(namesList); } // ... 其余业务逻辑 } catch (InterruptedException e) { e.printStackTrace(); return ""; } }
内容的提问来源于stack exchange,提问作者Daniel Janz
相关产品推荐
相关产品推荐

