1

我有一个使用网络的 Java 游戏,我有一个客户端(使用套接字)从 ObjectInputStream 中获取对象,在它自己的线程中运行。

Client.java

        Object input = null;
        while(true) {
            input = in.readObject();
            if(input != null) {
                listener.gotObject(input);
            }
        }

这工作得很好。获取该对象并将其传递给侦听器,该侦听器是一个链接到我的主 GameApp 类的类。

从侦听器(NetControl.java):

public void gotObject(Object o) {
    System.out.println(o);
    app.gotObject(o);
}

“app”是处理所有接收到的新对象并处理它们的实例。

从应用程序(GameApp.java)(编辑:非抽象CardGameApp.java提供更大的上下文):

public void gotObject(Object o) {
    // select instance:
    if(o instanceof GameList) {
        GameList gameList = (GameList) o;
        System.out.println("gamelist: " + gameList);
        this.lobbyControl.gotGameList(gameList);
    }
}

我已经在调试器中运行了这段代码,一次一步,它运行良好。当我正常运行它时,我得到一个空指针(输出如下:)

Game ID: 0. Name: game1. Players: 1 / 1. // the object, as it is printed in Client.java

gamelist: Game ID: 0. Name: game1. Players: 1 / 1. // the object, as it is printed again in GameApp.java

Exception in thread "Thread-1" java.lang.NullPointerException
at com.lgposse.game.app.GameApp.gotObject(GameApp.java:61)
at com.lgposse.game.net.NetControl.gotObject(NetControl.java:47)
at com.lgposse.net.client.Client.run(Client.java:49)

现在,我看到该对象被打印了两次,所以我知道它已被接收……但我得到一个空指针。

我在函数中间添加了一个睡眠函数:

    else if(o instanceof GameList) {
        GameList gameList = (GameList) o;
        System.out.println("gamelist: " + gameList);
        try {
            Thread.sleep(1000); // sleep 100 still gave null pointer
        } catch (InterruptedException e) {}
        this.lobbyControl.gotGameList(gameList);
    }

并将其设置为睡眠一段时间,这一切终于奏效了。

知道为什么我需要像这样睡觉线程吗?有什么我应该做的不同的事情吗?我不确定为什么我能够在它仍然被认为是空的时候打印它。

编辑:添加了更多上下文。

4

3 回答 3

1

好吧,我首先要说发布的代码片段似乎有助于说明问题,但我不认为完整的图片已经绘制出来。我会要求更多的代码,以帮助获得完整的上下文。

话虽如此,我会提供以下指导:

  1. 不要依赖 java 的内置对象序列化。它很好并且易于使用,但在运行时可能非常不稳定且容易出错。我建议使用自定义对象序列化和反序列化方案。

  2. 根据您正在制作的游戏的范围,NIO 可能是一个更好的选择。如果您坚持使用常规 IO,那么请确保您有一个坚如磐石的线程管理器来正确处理处理套接字 IO 的线程。

..没有更多代码,这是我能提供的最多。

于 2012-03-16T01:08:20.183 回答
1

看起来lobbyControlnull,不是gameList。如果gameList为 null,则堆栈的顶部将是gotGameList()方法,而不是gotObject().

如果睡眠有助于解决问题,那么您必须在lobbyControl没有适当的并发保护措施的情况下操纵成员。在ObjectInputStream从流中完全读取对象之前,它不会返回对象,因此您的问题与没有完全读取对象无关。


更新:我无法遵循所有代码,但似乎对正在构造的对象的引用泄露给了一个线程(client中的NetControl),该线程在构造函数完成之前启动。如果真是这样,那就非常非常糟糕了。永远不应该让一个部分构造的对象对另一个线程可见。

于 2012-03-16T01:17:28.283 回答
0

只是为了改进我的评论...当我需要等待一个或多个线程完成时,我喜欢使用 java.util.concurrent.CountDownLatch。它非常简单:

//game class
public class DummyGame
{
    CountDownLatch signal;

    public DummyGame( CountDownLatch signal)
    {
        this.signal = signal;
    }
   public void run()
   {
      doLogic();
      signal.countDown();
   }
}

//game controller class

 public void run()
 { 
    while (! gameOver)
    {
       CountDownLatch signal = new CountDownLatch(1); //wait one thread to finish
       new thread(newGame(signal)).start();

       //wait for game run() to finish
        signal.await();

       updateInterface();
    }
 }

这只是一个想法,希望它有所帮助。

于 2012-03-16T01:22:10.750 回答