【问题标题】:Multi-threading and Java Swing Problem多线程和 Java Swing 问题
【发布时间】:2009-04-06 04:08:17
【问题描述】:

您好,我有一个运行良好的 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(); 
    }
} 

【问题讨论】:

    标签: java multithreading swing


    【解决方案1】:

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

    关于 Swing 程序的两件事:

    1. 永远不要在 Swing 事件线程中执行任何长任务,并且,
    2. 切勿在 Swing 事件线程之外操作任何 Swing 对象,除非正在使用的方法被明确记录为线程安全的。

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

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

    注意事项:

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

    你的代码是:

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • OP 一定是最初使用该代码来启动线程,无意中在调用者的线程中运行代码。是的,该代码在 OP 的代码中已过时。
      【解决方案3】:

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

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

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

      不过,这当然不是问题。

      这里有一个想法:在你创建和启动服务器的时候,直接在调用之后,添加一个打印或日志语句,即,

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

      看看你是否看到“到了!”信息。如果不是,那么您正在阻塞 GUI 线程。

      【讨论】:

      • 看看你是否看到“到了!”信息。如果没有,那么您正在阻止 GUI 线程。这就是问题。我添加了代码,它永远不会出现“到这里”消息。我不确定如何解决这个问题。
      • 好吧,那么您至少必须向我们展示该调用的代码;听起来这个线程正在启​​动。
      • 我在我的主类中创建了一个 Server 对象,我工作得很好……虽然 GUI 仍在运行,但我需要在 GUI 应用程序中创建 Server 对象
      【解决方案4】:

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

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

      SwingWorker 可能是更简洁的方法。您可以在此处找到有关它的更多信息:

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

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

      【讨论】:

      • 请注意,invokeLater() 实际上会在事件线程上运行被调用的代码。然而,SwingWorker 将帮助 OP。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-24
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 2011-09-19
      • 2012-05-21
      相关资源
      最近更新 更多