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

Java 为什么每次执行ActionListener时我的GUI窗口都会冻结?,java,multithreading,swing,user-interface,networking,Java,Multithreading,Swing,User Interface,Networking,每当单击GUI窗口上的按钮时,我将通过客户端1向服务器发送一条字符串消息“5”,服务器收到该消息并将其发送到另一个客户端,即客户端2,该客户端与客户端1连接到同一端口。单击按钮时,客户端2确实会收到字符串消息“5”,但问题是,一旦单击按钮,客户端1的整个GUI窗口就会冻结(无法单击任何内容)。客户端2也是如此,它在单击按钮时发送字符串消息“2”,但在单击按钮时GUI窗口也会冻结。为什么GUI窗口冻结 代码段: BufferedReader buffR; Socket sock = new Soc

每当单击GUI窗口上的按钮时,我将通过客户端1向服务器发送一条字符串消息“5”,服务器收到该消息并将其发送到另一个客户端,即客户端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(); 
    }
});
已编辑-其他代码段(接收部分): 这是一个可运行的方法,与上面的代码片段一起位于thread类中

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());
    }
}
您的
while(true)
正在Swing事件线程上被调用,这会将线程绑定,并阻止它执行绘制GUI和与用户交互的操作。此外,您还多次添加ActionListener,这没有什么意义,因为按下按钮时会调用每个ActionListener

解决方案:

  • 摆脱那个不必要和危险的while循环
  • 仔细阅读,然后努力遵守规则

  • 请注意,如果需要连续读取BufferedReader对象,则可以将其读取放在
    while(true)
    循环中,但不要在循环中添加任何ActionListener或对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的操作

    接受的一面是使用SwingWorker允许我们使用阻塞代码,
    buff.readLine()而不阻塞GUI。请注意,
    doInBackground()
    方法中的所有代码都是在后台线程中完成的,并且是在Swing事件线程(事件调度线程)之外完成的:

    //使用SwingWorker,因为我要发布字符串
    新SwingWorker(){
    受保护的Void doInBackground()引发异常{
    while(true){
    //从后台线程中的BufferedReader读入字符串
    字符串行=buffer.readLine();
    //发布字符串,以便在Swing事件线程EDT上使用
    出版(行);
    }            
    };
    //此代码在Swing事件线程EDT中调用
    受保护的void进程(java.util.List块){
    for(字符串行:块){
    //在我的JText区域中显示文本
    myTextArea.append(第+行“\n”);
    }
    };
    }.execute();
    
    您的
    while(true)
    正在Swing事件线程上被调用,这会捆绑线程并阻止它执行绘制GUI和与用户交互的操作。此外,您还多次添加ActionListener,这没有什么意义,因为按下按钮时会调用每个ActionListener

    解决方案:

  • 摆脱那个不必要和危险的while循环
  • 仔细阅读,然后努力遵守规则

  • 请注意,如果需要连续读取BufferedReader对象,则可以将其读取放在
    while(true)
    循环中,但不要在循环中添加任何ActionListener或对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的操作

    接受的一面是使用SwingWorker允许我们使用阻塞代码,
    buff.readLine()而不阻塞GUI。请注意,
    doInBackground()
    方法中的所有代码都是在后台线程中完成的,并且是在Swing事件线程(事件调度线程)之外完成的:

    //使用SwingWorker,因为我要发布字符串
    新SwingWorker(){
    受保护的Void doInBackground()引发异常{
    while(true){
    //从后台线程中的BufferedReader读入字符串
    字符串行=buffer.readLine();
    //发布字符串,以便在Swing事件线程EDT上使用
    出版(行);
    }            
    };
    //此代码在Swing事件线程EDT中调用
    受保护的void进程(java.util.List块){
    for(字符串行:块){
    //在我的JText区域中显示文本
    myTextArea.append(第+行“\n”);
    }
    };
    }.execute();
    
    您的
    while(true)
    正在Swing事件线程上被调用,将线程绑定起来,并阻止它执行绘制GUI和与u交互的操作