【问题标题】:Java JButton actionPerformed freezingJava JButton action执行冻结
【发布时间】:2013-06-21 18:10:59
【问题描述】:

我一直在玩 Java,我正在尝试制作一个程序来开始 ping 地址并打印出“ms”。

我有一个 JButton:

JButton start = new JButton("START");
    start.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e){

            try {
                doCommand();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }
    });

doCommand() 方法如下所示:

public static void doCommand() throws IOException{
    String s = null;
    ProcessBuilder pb = new ProcessBuilder(commands); //definiramo procesBuilder
    Process proces = pb.start(); //zaženemo proces (vrne Process)

    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proces.getInputStream()));   //Branje outputa procesa
    BufferedReader stdError = new BufferedReader(new InputStreamReader(proces.getErrorStream()));   //Branje error outputa

    while((s = stdInput.readLine()) != null){   //dokler output obstaja (ni error)
        int dvop = s.indexOf(":") + 16;
        if(s.startsWith("Reply")){
            s=s.substring(dvop);
            int pres = s.indexOf(" ");
            s=s.substring(0,pres-2);
            //System.out.println(s);
            label.setText(s);
        }
    }
    while((s = stdError.readLine()) != null){   //dokler error obstaja
        System.out.println(s);
    }

}

发生的情况是,每次我按下按钮时,程序都会冻结而没有任何反应,我什至无法以“正常”方式关闭它...... 我想我做错了什么......

【问题讨论】:

  • 重的东西应该用SwingWorker,那么有标签的类就是swingWorker的观察者,swingWorker通知工作结束,你在EDT更新你的标签

标签: java swing concurrency jbutton event-dispatch-thread


【解决方案1】:

对 StinePike 答案的详细说明。正如他所说,actionPerformed() 方法将在主 GUI 事件线程上运行,这意味着 GUI 可能仅在 actionPerformed() 返回时响应。您发布的代码使用了可能无法快速完成的阻塞 I/O。

尤其是这一行:

while((s = stdInput.readLine()) != null){

哪些阻塞来自标准输入的输入。当此块生效时,GUI 变得无响应,因为方法 actionPerformed() 尚未返回。

既然你说这段代码的目的是从外部应用程序返回响应,那么外部应用程序可能是:在 stderr 上返回了一些东西,或者在另一个条件下阻塞。

一个可能的解决方案如下:

doCommand() 方法:

public static void doCommand() throws IOException{
    String s = null;
    ProcessBuilder pb = new ProcessBuilder(commands); //definiramo procesBuilder
    Process proces = pb.start(); //zaženemo proces (vrne Process)

    final BufferedReader stdInput = new BufferedReader(new InputStreamReader(proces.getInputStream()));   //Branje outputa procesa
    final BufferedReader stdError = new BufferedReader(new InputStreamReader(proces.getErrorStream()));   //Branje error outputa

    Thread readStdIn = new Thread(new Runnable(){
        public void run(){
            try{
                while((s = stdInput.readLine()) != null){   //dokler output obstaja (ni error)
                    int dvop = s.indexOf(":") + 16;
                    if(s.startsWith("Reply")){
                        s=s.substring(dvop);
                        int pres = s.indexOf(" ");
                        s=s.substring(0,pres-2);
                        //System.out.println(s);

                        //Execute on the main AWT thread (I'm assuming 'label' is the name of one of your GUI components)
                        SwingUtilities.invokeLater(new Runnable(){
                            public void run(){
                                label.setText(s);
                            }
                        });
                    }
                }
            }catch(IOException ex){
                //Handle This
            }
        }
    });

    Thread readStdErr = new Thread(new Runnable(){
        public void run(){
            try{
                while((s = stdError.readLine()) != null){   //dokler error obstaja
                    System.out.println(s);
                }
            catch(IOException ex){
                //Handle This Too
            }
        };
    });

    readStdIn.start();
    readStdErr.start();
}

上面的代码将产生两个单独的线程,它们将读取 stdin 和 stderr 的内容并单独处理它们。由于块 I/O 已从主 GUI 线程移植,因此即使外部应用程序执行一些奇怪的操作(例如冻结或死锁),GUI 也应该继续响应。

注意:这将允许按下按钮,即使外部应用程序没有完成执行。

编辑:为 BufferedReader.readLine() 添加了缺失的 try-catch 块

【讨论】:

    【解决方案2】:

    actionPerformed 方法在主线程上执行。因此,如果您在其中执行繁重的任务,那么它将冻结 gui。最好使用不同的线程。

    【讨论】:

      【解决方案3】:

      你应该使用 SwingWorker 来完成这个任务,在这里你有一个很好的教程和一个例子 File Swing Worker example

      【讨论】:

        猜你喜欢
        • 2019-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-27
        • 2014-08-15
        • 1970-01-01
        相关资源
        最近更新 更多