为什么每次执行ActionListener时我的GUI窗口都会冻结?

时间:2014-11-01 23:27:40

标签: java multithreading swing user-interface networking

我发送了一个字符串消息" 5"每当点击GUI窗口上的按钮并且服务器接收到该消息并将其发送到与客户端1相同的端口的客户端2时,通过客户端1到服务器。当单击该按钮时,客户端2确实收到字符串消息" 5",但问题是单击按钮后,客户端1的整个GUI窗口冻结(无法点击任何内容)。对于客户端2也是如此,其发送字符串消息" 2"单击按钮时,单击按钮时GUI窗口也会冻结。为什么GUI窗口会冻结?

代码段:

BufferedReader buffR;
Socket sock = new Socket(nameHost, portNum); 
PrintWriter printW = new PrintWriter(sock.getOutputStream()); 
this.buffR = new BufferedReader(new InputStreamReader(sock.getInputStream()));

button.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent ae){
        printW.println("5");
        printW.flush(); 
    }
});

已编辑 - 附加代码段(接收部分): 这是一个可运行的方法,它与上面的代码片段一起在线程类中。

public void run() { 

    try{
        while(true){

            String line = br.readLine(); 
            int update = Integer.parseInt(line);
            current-= update; //int current = 0 and update is the number received from the other client, which is "5"

            JLabel newNumber = new JLabel(current); //Assign the newNumber JLabel with the current number

            jp.remove(number); //Remove the JLabel 'number' which initially shows "50" 
            jp.revalidate();
            jp.repaint();
            jp.add(newNumber); //Replace the JLabel 'number' with 'newNumber'

            if(update == 0){
                JOptionPane.showMessageDialog(jf, "Reached 0", "Message", JOptionPane.INFORMATION_MESSAGE);
                System.exit(0);
        }
    }

    } catch (IOException ioe){
        System.out.println("ioe in ChatClient.run: " + ioe.getMessage());
    }
}

1 个答案:

答案 0 :(得分:3)

您正在Swing事件线程上调用while (true),占用线程并阻止它执行绘制GUI和与用户交互的操作。此外,您多次添加ActionListener,因为按下按钮时将调用每个ActionListener,因此没有多大意义。

解决方案:

  1. 摆脱那种不必要且危险的循环。
  2. 阅读Swing Threading,然后努力遵守规则。

  3. 请注意,如果您需要连续读取BufferedReader对象,那么请将其读取放在while (true)循环内,但不要添加任何ActionListeners或对状态进行任何更改循环内部的Swing对象。相反,我使用SwingWorker对象,在while (true)方法中使用doInBackground()循环,这样它肯定会在后台线程中运行,然后我会更新显示使用SwingWorker的发布/处理方法对的GUI,以便以线程安全的方式将字符串推入GUI。我上面给出的链接将帮助您实现这一目标。


    修改
    您的代码可能类似于:

    设置按钮的ActionListener

      // add this ActionListener once and only once
      button.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            // get the line of text from the GUI
            String text = myTextField.getText();
    
            // send the text out to the socket via the PrintWriter
            printW.println(text);
         }
      });
    

    请注意,最好使用AbstractAction,然后使用相同的对象设置JButton和JTextArea的Action。

    接受方面的事情将涉及使用SwingWorker来允许我们使用阻塞代码buffR.readLine();而不阻止GUI。请注意,doInBackground()方法中的所有代码都是在后台线程中完成的,而不是Swing事件线程,EDT(事件调度线程):

      // use a <Void, String> SwingWorker since I want to publish a String
      new SwingWorker<Void, String>() {
         protected Void doInBackground() throws Exception {
            while (true) {
    
               // read in a String from the BufferedReader in background thread
               String line = buffR.readLine();
    
               // publish String so it can be used on Swing event thread, the EDT 
               publish(line);
            }            
         };
    
         // this code is called in the Swing event thread, the EDT
         protected void process(java.util.List<String> chunks) {
            for (String line : chunks) {
               // display text in my JTextArea
               myTextArea.append(line + "\n");
            }
         };
      }.execute();