多线程和Java Swing问题

时间:2009-04-06 04:08:18

标签: java multithreading swing

您好我有一个工作正常的GUI应用程序。我创建了一个套接字服务器当我在程序中创建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(); 
    }
} 

5 个答案:

答案 0 :(得分:11)

您的Server构造函数可能无限期地阻止accept()

关于Swing程序的两件事:

  1. 永远不要在Swing事件线程中执行任何长任务,
  2. 永远不要在Swing事件线程之外操作任何Swing对象,除非正在使用的方法被明确记录为线程安全。
  3. 这意味着如果服务器是从Swing事件线程启动的 - 也就是说,如果它是为响应按钮单击等而启动的 - 那么是的,你必须为你的Server对象生成另一个线程。否则,您保证在您的线程退出之前阻止Swing事件线程。

    你说即使你为服务器生成另一个线程,你的应用程序仍然停止响应?确保您正在呼叫Thread.start()而不是run(),否则您将意外地通过在您自己的线程中运行“新线程”来阻止自己。

    注意:

    1. 我看到你在run()循环中执行Thread.sleep(0);。这不能保证做任何事情。如果你有一台CPU机器,这可以公平地实现为无操作,允许同一个线程继续运行。
    2. 您确实希望isConnectedvolatile - 否则无法保证除了更改之外的任何线程都会看到对此变量的更改。
    3. 您不会在任何地方将isConnected设置为false,因此您的run()将一直运行,直到JVM停止或直到该线程采用RuntimeException。
    4. It is discouraged在构造函数中启动Threads。 (见Java Concurrency In Practice。)
    5. 在您使用主题的accept方法之前,您不希望在ServerSocket上使用run()!否则,您的构造函数将阻止等待连接,并且不会将控制权返回给事件线程!
    6. 您的构造函数中包含以下代码:
    7. 您的代码是:

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

      当您thread.run()没有注释掉时,您开始新线程!为此,您需要执行thread.start()。相反,你在调用构造函数的同一个线程中运行这个新的Thread(它永远不会停止,因为上面的原因#3)。现在编写代码的方式,所有IOExceptions都会被记录,但是否则会被吞噬。您可能希望在isConnected以及false中将IOException设置为closeConnection()

答案 1 :(得分:4)

问题是您的Server构造函数是阻塞的。 cosntructor不应该进行任何阻塞调用(实际上它应该尽可能少)。阻塞调用应该由run()或run()调用。

另请注意,您创建一个新的Thread()是一个似乎没有任何用途的构造函数。

答案 2 :(得分:1)

您的服务器类看起来似乎合理(我实际上没有编译它,但它看起来似乎是合理的。)如果您的GUI变得没有响应,这几乎意味着GUI线程无法获得控制权。要了解原因,我们真的需要查看创建它的代码 - 它在哪里以及它是如何工作的。

你的基本想法是正确的,尽管你可以将它缩短为

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

但这肯定不是问题。

这是一个想法:在您创建并启动服务器时,在调用后直接添加打印或日志记录语句,即

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

看看你是否“看到了!”信息。如果没有,那么你就是阻止了GUI线程。

答案 3 :(得分:0)

您可以使用Java 6中提供的SwingWorker。如果您需要在以前版本的Java中使用它,您可以随时使用:

https://swingworker.dev.java.net/

SwingWorker可能是更清洁的方法。你可以在这里找到更多相关信息:

http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html

然后,您可以编写新的ServerSwingWorker内部类来包装该功能或使用匿名内部类。

答案 4 :(得分:0)

细节,做

    new Thread(new Server())

无效,因为“new Server()”仍然在实际的Thread中执行。

应该是这样的:

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            new Server();
        }
    });
    t.start();