Java swing GUI卡死冻结

5
我正在编写一个使用套接字(sockets)的Java客户端/服务器GUI应用程序,以下是问题:
我有一个按钮来开始监听指定的端口:
按钮actionPerformed方法
private void listenButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
    int port = Integer.parseInt(portTextfield.getText(), 10);

    try {
        socket.listen(port);
    } catch (IOException ex) {
    }
}

这里是socket.listen方法。

public static void listen() throws IOException {
    ServerSocket ss = new ServerSocket(port);

    while (true)
        new socket(ss.accept());
}

"socket"类继承自"Thread"
因此,在ss.accept()返回一个值后,它会在单独的线程中创建新的socket实例。

点击按钮后,GUI会冻结,因为socket.listen方法内部有一个无限循环。我该如何避免这种情况?


请问您的问题是什么?为了更快地获得帮助,请发布一个最小可重现代码示例(SSCCE)。 - mKorbel
1
阅读有关 Swing 中的并发 的内容。 - assylias
参考这里有一个工作示例:https://dev59.com/kHA75IYBdhLWcg3wg5js#3245805。 - trashgod
5个回答

5
只要你的
new socket(ss.accept());

立即返回,您只需要更改您的
while (true)

这会将EDT(事件分派线程)置于无限循环中,导致GUI无响应。因此,请删除此行代码。
如果不能删除,请使用SwingWorker类(http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#process(java.util.List)创建一个扩展SwingWorker的嵌套类。在listenButtonActionPerformed(java.awt.event.ActionEvent evt)方法中创建对象后,只需调用swingWoker.execute();即可。
请参阅教程:http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html 永远不要从Swing EDT中创建新线程并运行它们

5
您的设计中存在两个问题:
  1. ss.accept() 是一个阻塞调用,因此您的用户界面将会冻结,直到有一个传入的连接。
  2. 不要在EDT中运行 while(true) 循环。

相反,应该执行以下操作:

  • 当按钮被点击时,创建一个线程来开始监听传入的连接。
  • 每当有一个传入的连接时,创建另一个线程来处理它。

4

看这个链接:http://javarevisited.blogspot.ro/2012/02/what-is-blocking-methods-in-java-and.html

1) 如果你正在编写GUI应用程序,可能是在Swing中,请勿在事件分派线程或事件处理程序中调用阻塞方法。例如,如果单击按钮时要读取文件或打开网络连接,请不要在actionPerformed()方法中执行此操作,而是创建另一个工作线程来执行该作业,并从actionPerformed()返回。 这将使您的GUI响应且流畅,但这取决于设计,如果操作需要用户等待,则考虑使用invokeAndWait()进行同步更新。

使用多个线程:http://javarevisited.blogspot.ro/2011/02/how-to-implement-thread-in-java.html


3

您需要使用多线程。如果是我,我会将GUI代码和服务器代码分离,当按下按钮时,我只需启动Server代码作为新线程。

您的代码正在冻结GUI,因为所有事件都在事件调度线程(EDT)上执行,这是负责处理所有GUI内容和相应事件的线程。如果您阻塞它、停止它或在其中添加循环,将影响其性能。


2
尝试以下方法... 1. 在获取初始连接时可能会出现延迟,因此首先创建一个空socket,然后尝试连接到服务器。
   `Socket s = new Socket();`

   `s.connect(new InetSocketAddress("ip_addr",port_nos),1000);`

2. 其次,始终将非 UI 工作保持在您的 UI 线程之外。

这是我的服务器-客户端通信示例。

客户端代码:

public class ClientWala {

    public static void main(String[] args) throws Exception{

        Boolean b = true;
    Socket s = new Socket();
    s.connect(new InetSocketAddress("127.0.0.1", 4444),1000);

    System.out.println("connected: "+s.isConnected());


    OutputStream output = s.getOutputStream();
    PrintWriter pw = new PrintWriter(output,true);

    // to write data to server
    while(b){

        if (!b){

             System.exit(0);
        }

        else {
            pw.write(new Scanner(System.in).nextLine());
        }
    }


    // to read data from server
    InputStream input   = s.getInputStream();
    InputStreamReader isr = new InputStreamReader(input);
    BufferedReader br = new BufferedReader(isr);
    String data = null;

    while ((data = br.readLine())!=null){

        // Print it using sysout, or do whatever you want with the incoming data from server

    }




    }
}

服务器端代码:

import java.io.*
import java.net.*;


public class ServerTest {

    ServerSocket s;

    public void go() {

        try {
            s = new ServerSocket(44457);

            while (true) {

                Socket incoming = s.accept();
                Thread t = new Thread(new MyCon(incoming));
                t.start();
            }
        } catch (IOException e) {

            e.printStackTrace();
        }

    }

    class MyCon implements Runnable {

        Socket incoming;

        public MyCon(Socket incoming) {

            this.incoming = incoming;
        }

        @Override
        public void run() {

            try {
                PrintWriter pw = new PrintWriter(incoming.getOutputStream(),
                        true);
                InputStreamReader isr = new InputStreamReader(
                        incoming.getInputStream());
                BufferedReader br = new BufferedReader(isr);
                String inp = null;

                boolean isDone = true;

                System.out.println("TYPE : BYE");
                System.out.println();
                while (isDone && ((inp = br.readLine()) != null)) {

                    System.out.println(inp);
                    if (inp.trim().equals("BYE")) {
                        System.out
                                .println("THANKS FOR CONNECTING...Bye for now");
                        isDone = false;
                        s.close();
                    }

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

        }

    }

    public static void main(String[] args) {

        new ServerTest().go();

    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接