如何在JDialog上实现阻塞式readString方法,等待用户输入后返回内容
如何在JDialog上实现阻塞式readString方法,等待用户输入后返回内容
嘿,我完全懂你的需求——你想要让readString()方法像控制台的输入方法那样,调用后就阻塞住,直到用户在JTextArea里输入内容并按下回车,再返回输入的文本,而且还不能修改这个方法的原型对吧?
先说说你原来代码里的问题:那个空的while(userInput==null)循环会直接卡死Swing的事件调度线程(EDT)。因为Swing是单线程模型,所有UI操作和事件处理(比如按钮点击、键盘输入)都在EDT上执行。当你点击按钮触发readString()时,这个空循环会占着EDT不放,导致KeyListener根本没机会处理回车事件,自然userInput永远不会被赋值,程序就彻底僵住了。
下面给你两个可行的解决方案,都不会卡死EDT,而且完全符合你的方法原型要求:
方案一:用CountDownLatch(推荐,更简洁)
CountDownLatch是Java并发包里的工具类,可以让一个线程等待其他线程完成某个操作。这里我们用它来让readString()阻塞,直到用户按下回车。
修改后的Consola类代码:
import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.concurrent.CountDownLatch; public class Consola { private final JDialog dialog; private final JTextArea textArea; private String userInput; private CountDownLatch inputLatch; // 用于阻塞的 latch 对象 public Consola(JFrame mainFrame) { dialog = new JDialog(mainFrame); textArea = new JTextArea(); textArea.addKeyListener(new EscuchaKey()); dialog.add(new JScrollPane(textArea), BorderLayout.CENTER); dialog.setSize(300, 300); dialog.setVisible(true); } public String readString() { // 重置输入状态 userInput = null; textArea.setText(""); // 初始化一个计数为1的Latch inputLatch = new CountDownLatch(1); try { // 阻塞当前线程,直到countDown()被调用 inputLatch.await(); } catch (InterruptedException e) { // 恢复线程的中断状态,这是良好的并发实践 Thread.currentThread().interrupt(); return null; // 中断时可以返回null或自定义处理 } return userInput; } class EscuchaKey extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { userInput = textArea.getText().trim(); // 释放阻塞的线程 if (inputLatch != null) { inputLatch.countDown(); } } } } }
方案二:用Object的wait()/notify()(传统并发方式)
如果你不想用并发包的类,可以用Java原生的对象锁机制,通过wait()让线程等待,notify()唤醒线程。
修改后的Consola类代码:
import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; public class Consola { private final JDialog dialog; private final JTextArea textArea; private String userInput; private final Object lock = new Object(); // 自定义锁对象 public Consola(JFrame mainFrame) { dialog = new JDialog(mainFrame); textArea = new JTextArea(); textArea.addKeyListener(new EscuchaKey()); dialog.add(new JScrollPane(textArea), BorderLayout.CENTER); dialog.setSize(300, 300); dialog.setVisible(true); } public String readString() { synchronized (lock) { // 重置输入状态 userInput = null; textArea.setText(""); try { // 释放锁并等待,直到被notify()唤醒 lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } return userInput; } } class EscuchaKey extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { synchronized (lock) { userInput = textArea.getText().trim(); // 唤醒等待的线程 lock.notify(); } } } } }
关键注意事项
- 绝对不要用空循环:空循环会占用100%CPU,而且彻底卡死EDT,导致UI完全无响应。
- 处理中断异常:
await()和wait()都会抛出InterruptedException,一定要恢复线程的中断状态,避免后续逻辑出错。 - 处理窗口关闭场景:如果用户关闭了JDialog,你可以给dialog加个
WindowListener,在窗口关闭时调用countDown()或notify(),避免线程一直阻塞。 - 状态重置:每次调用
readString()都要重置userInput和阻塞工具的状态,避免多次调用之间的干扰。
你的ConsolaTest类完全不用修改,直接用这两个修改后的Consola类就能实现你想要的效果——点击按钮后,程序会等待用户在JDialog的文本框里输入内容并按回车,然后输出输入的文本。
备注:内容来源于stack exchange,提问作者user3216993
相关产品推荐
相关产品推荐

