多线程和Java Swing问题

3

你好,我有一个GUI应用程序,运行良好。我创建了一个socket服务器。但是当我在程序中创建Server类的新对象时,GUI应用程序停止响应。

这是我的服务器类。如果我执行

Server s = new Server();

我的主要应用程序停止工作了。我该怎么添加它?创建一个新线程吗?我尝试过了。

Thread t = new Thread(new Server());
t.start();

但问题仍然存在。请您帮忙解决,非常感谢。

package proj4;

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

public class Server implements Runnable { 
    ServerSocket       serverSocket = null;
    Socket             clientSocket = null;
    ObjectOutputStream out          = null;
    ObjectInputStream  in           = null;
    int                port;
    static int         defaultPort  = 30000;
    boolean            isConnected  = false;
    Thread             thread;
    DataPacket         packet       = null;

    public Server(int _port) {
        try {
            serverSocket = new ServerSocket(_port);
            serverSocket.setSoTimeout(1000*120);  //2 minutes time out     
            isConnected = true;
            System.out.println("server started successfully");
            thread = new Thread(this);
            thread.setDaemon(true);
            //thread.run();
        } catch (IOException e) {
            System.err.print("Could not listen on port: " + port);
            System.exit(1);
        }
        try {
            System.out.println("Waiting for Client");
            clientSocket = serverSocket.accept();
            System.out.println("Client Connected");
            thread.run();
        } catch (IOException e) {
            System.err.println("Accept failed.");
            System.exit(1);
        }
        try {
            out = new ObjectOutputStream(clientSocket.getOutputStream());
            System.out.println("output stream created successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            in = new ObjectInputStream(clientSocket.getInputStream());
            System.out.println("input stream created successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Server() {
        this(defaultPort); //server listens to port 30000 as default
    }

    public void run() {
        System.out.println("Thread running, listening for clients");//debugging purposes
        while (isConnected) {
            try {
                packet = this.getData();
                Thread.sleep(0);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    } 

    public DataPacket getData() {
        try {
            packet = (DataPacket)in.readObject();
        } catch (Exception ex)  {
            System.out.println(ex.getMessage());
        }
        return packet;
    }

    public void sendData(DataPacket dp) {
        try {
            out.writeObject(dp);
        } catch (IOException e) {
            e.printStackTrace();
        } 
        try {
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void closeConnection() throws IOException {
        out.close(); 
        in.close(); 
        clientSocket.close(); 
        serverSocket.close(); 
    }
} 
4个回答

11

您的 Server 构造函数在 accept() 中被阻塞,可能无限期地阻塞。

关于 Swing 程序有两件事:

  1. 永远不要在 Swing 事件线程中执行长时间任务,且
  2. 除非使用的方法已明确文档化为线程安全,否则不要在 Swing 事件线程之外操纵任何 Swing 对象。

这意味着,如果服务器是从 Swing 事件线程启动的,也就是说,如果它是作为按钮单击等响应而启动的,则必须为您的 Server 对象生成另一个线程。否则,您将保证 Swing 事件线程会被阻塞,直到您的线程退出。

您说,即使为服务器生成了另一个线程,您的应用程序仍然停止响应?请确保您调用的是 Thread.start() 而不是 run(),否则您将意外地通过在自己的线程中运行 "new Thread" 来继续阻塞自己。

注意:

  1. 我看到您在 run() 循环中执行了 Thread.sleep(0);。这并不能保证做任何事情。如果您有一台单 CPU 的机器,则可以将其实现为无操作,允许同一线程继续运行。
  2. 您真正希望 isConnectedvolatile 的 — 否则没有保证该变量的更改将被除修改它的线程之外的任何线程看到。
  3. 您没有在任何地方将 isConnected 设置为 false,因此您的 run() 将一直运行,直到 JVM 停止或该线程抛出 RuntimeException。
  4. 不建议 在构造函数中启动线程。(参见Java Concurrency In Practice。)
  5. 在您的构造函数中,您不想在 ServerSocket 上进行 accept,直到您在线程的 run() 方法中!否则,您的构造函数将阻塞等待连接,并且不会将控件返回给事件线程!
  6. 您在构造函数中有以下代码:

您的代码是:

thread = new Thread(this);
thread.setDaemon(true);
//thread.run();

如果没有注释掉thread.run(),那么你实际上没有启动一个新的线程!要启动一个新的线程,你需要使用thread.start()。相反,你是在调用构造函数的相同线程中运行这个新线程(由于上述原因,它永远不会停止)。按照你代码的编写方式,所有的IOException都被记录在日志中,但是没有其他任何处理。你可能想要在任何IOException发生时将isConnected设置为false,并在closeConnection()中执行相同操作。


4
问题在于您的Server构造函数是阻塞的。构造函数不应该做任何阻塞调用(实际上它应该尽可能地少做)。阻塞调用应该由run()或run()调用的某个内容进行。

另外请注意,您在构造函数中创建了一个没有任何目的的新Thread()。


OP 最初必须使用该代码来启动线程,无意中在调用者的线程中运行该代码。是的,在 OP 的代码中,该代码已经过时了。 - Eddie

1

你的服务器类看起来是可行的(我还没有实际编译它,但看起来是可行的)。如果你的GUI变得无响应,那基本上意味着GUI线程没有得到控制。为了查看原因,我们真的需要查看创建此代码的位置和方式。

你的基本想法是正确的,虽然你可以将其缩短为

(new Thread(new Server()).start();

那肯定不是问题。

这里有一个想法:在你创建并启动服务器的时候,在调用之后直接添加一个打印或记录语句,例如,

(new Thread(new Server()).start();
System.err.println("Got here!");

并查看是否看到“已到达!”消息。如果没有,那么您正在阻塞GUI线程。


请查看并确认是否能够看到“Got here!”的消息。如果没有,那么您可能正在阻塞GUI线程。这就是问题所在。我添加了代码,但从未看到“Got here”消息。我不确定如何解决这个问题。 - user69514
那你就得给我们展示至少关于这个调用的代码,听起来像是这个线程开始了。 - Charlie Martin
我在主类中创建了一个Server对象,并且它很好地运行了.. 只要GUI仍在运行,但我需要在GUI应用程序内部创建Server对象。 - user69514

0

请注意,invokeLater()实际上会在事件线程上运行被调用的代码。但是SwingWorker将有助于OP。 - Eddie

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