Java GUI监听器中Thread.sleep致界面冻结问题及Shell命令执行咨询
解决你的两个Java问题:Swing界面冻结+执行Shell命令
嘿,我来帮你搞定这两个问题!先从你遇到的倒计时界面冻结说起,再讲Java执行Shell命令的方法。
一、解决Swing倒计时界面冻结的问题
你遇到的界面冻结是Swing单线程模型导致的:Swing的所有UI更新和事件处理都在**事件调度线程(EDT)**里执行。你在ActionListener的actionPerformed方法里直接用Thread.sleep(1000),会把EDT线程阻塞住——这个线程被卡住了,自然没法更新界面、响应其他按钮操作,所以界面就冻住了。
解决方案:用javax.swing.Timer替代Thread.sleep
Swing Timer专门用来在EDT线程里定时执行任务,不会阻塞UI线程。下面是修改后的按钮点击事件代码:
butt1.addActionListener(new ActionListener(){ private int remainingSeconds = 600; // 10分钟=600秒 private Timer countdownTimer; public void actionPerformed(ActionEvent evt){ // 避免重复点击启动多个定时器 if(countdownTimer != null && countdownTimer.isRunning()){ return; } countdownTimer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { remainingSeconds--; if(remainingSeconds <= 0){ countdownTimer.stop(); lab1.setText("时间到!"); // 这里可以加闹钟触发的逻辑,比如播放声音 } else { // 格式化显示为分:秒,比如9:59 int minutes = remainingSeconds / 60; int seconds = remainingSeconds % 60; lab1.setText(String.format("%d:%02d", minutes, seconds)); } } }); countdownTimer.start(); } });
这样修改后,定时器每隔1秒在EDT线程里触发一次更新,既不会阻塞界面,还能正确刷新剩余时间。另外还加了重复点击的判断,防止启动多个定时器。
二、Java中执行Shell命令的方法
Java有两种常用的方式执行Shell命令:Runtime.getRuntime().exec()和ProcessBuilder,后者更推荐,因为更灵活。
1. 使用Runtime.getRuntime().exec()
这是比较传统的方式,适合简单命令:
try { // 执行ls命令(Windows换成dir) Process process = Runtime.getRuntime().exec("ls"); // 读取命令输出 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 等待命令执行完成,获取退出码 int exitCode = process.waitFor(); System.out.println("命令执行完成,退出码:" + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); }
注意:如果命令有参数,最好用字符串数组的形式传递,比如exec(new String[]{"ls", "-l"}),避免空格解析问题。
2. 使用ProcessBuilder(推荐)
ProcessBuilder提供了更多控制,比如设置工作目录、环境变量,还能合并错误流和输出流:
try { // 构建命令,比如执行ping百度 ProcessBuilder pb = new ProcessBuilder("ping", "www.baidu.com"); // 设置工作目录(可选) pb.directory(new File("/your/work/dir")); // 合并错误流到输出流,方便统一读取 pb.redirectErrorStream(true); Process process = pb.start(); // 读取输出 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } int exitCode = process.waitFor(); System.out.println("命令执行完成,退出码:" + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); }
你的原代码整理
最后把你提供的代码用规范的代码块整理一下:
public class Budik extends JFrame{ private JLabel lab1,lab2; private JButton butt1,butt2,butt3; private JTextField t1,t2; private JSpinner spinner; private JCheckBox cx1,cx2; private JList list; private JPanel p1,p2,p3,p4,p5; private JPasswordField pass1,pass2; public static void main(String[] args) { Budik okno = new Budik(); okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); okno.setVisible(true); okno.pack(); okno.setLocationRelativeTo(null); okno.setResizable(false); } public Budik() { super("Countdown"); setLayout(new BorderLayout()); // p1 GridLayout el = new GridLayout(2,1); p1 = new JPanel(el); add(p1,BorderLayout.NORTH); lab1 = new JLabel("Welcome",SwingConstants.CENTER); lab2 = new JLabel("Created by WajFaj",SwingConstants.CENTER); lab1.setFont(new Font("Serif", Font.PLAIN, 36)); p1.add(lab1); p1.add(lab2); //p2 p2 = new JPanel(); add(p2,BorderLayout.WEST); butt1 = new JButton("START"); p2.add(butt1); //p3 p3 = new JPanel(); add(p3,BorderLayout.CENTER); butt2 = new JButton("STOP"); p3.add(butt2); //p4 p4 = new JPanel(); add(p4,BorderLayout.EAST); butt3 = new JButton("RESET"); p4.add(butt3); //p5 p5 = new JPanel(); add(p5,BorderLayout.SOUTH); butt1.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent evt){ for(int i=600; i>0;i-- ){ try{ Thread.sleep(1000); String cc = ""+i; lab1.setText(cc); } catch(InterruptedException e){ System.out.println("Chyba!"); } } } }); } }
内容的提问来源于stack exchange,提问作者Zdeněk Krast
相关产品推荐
相关产品推荐

