我有一个使用网络的Java游戏,我有一个客户端(使用Socket)从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)
现在,我看到对象被打印了两次,所以我知道它已被收到......但我得到一个空指针。
我在函数中间添加了一个sleep函数:
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);
}
让它睡了一会儿,这一切终于奏效了。
知道为什么我需要像这样睡觉吗?有什么我应该做的不同吗?我不知道为什么我能够打印对象,因为它仍然被认为是空的。
编辑:添加了更多上下文。
答案 0 :(得分:1)
好吧,我首先要说的是,发布的代码片段似乎有助于说明问题,但我不认为完整的图片是绘制的。我会要求更多代码,以帮助获得完整的上下文。
话虽如此,我会提供以下指导:
不要依赖java的内置对象序列化。这很好,而且 易于使用,但在运行时可能非常不稳定且容易出错。 我建议自定义对象序列化和反序列化 方案
根据您正在制作的游戏范围,NIO可能是一个 netter选择。如果你坚持使用常规IO,那么请确保你 有一个坚如磐石的线程管理器到位,以妥善处理 处理套接字IO的线程。
..没有更多代码,这是我能提供的最多。
答案 1 :(得分:1)
看起来lobbyControl
是null
,而不是gameList
。如果gameList
为空,则堆栈顶部为gotGameList()
方法,而不是gotObject()
。
如果睡眠有助于解决问题,那么您必须在没有适当的并发安全措施的情况下操纵lobbyControl
成员。 ObjectInputStream
在从流中完全读取之前不会返回对象,因此您的问题与未完全读取对象无关。
更新:我无法遵循所有代码,但似乎对正在构造的对象的引用泄露给一个线程(client
中的NetControl
}),它在构造函数完成之前启动。如果是这种情况,则非常非常糟糕。您应该从不允许部分构造的对象对另一个线程可见。
答案 2 :(得分: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();
}
}
这只是一个想法,希望它有所帮助。