1

ネットワークを使用する 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);
    }
}

このコードをデバッガーで 1 ステップずつ実行したところ、完全に動作しました。ただし、通常どおり実行すると、null ポインターが返されます (出力は次のとおりです:)

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)

ここで、オブジェクトが 2 回印刷されているのがわかるので、受信したことはわかりますが、null ポインターが返されます。

関数の途中にスリープ関数を追加しました。

    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);
    }

そして、しばらくスリープ状態にすると、ようやくすべてが機能しました。

このようにスレッドをスリープ状態にする必要がある理由は何ですか? 何か違うことをしなければならないことはありますか?オブジェクトがまだnullと見なされているときにオブジェクトを印刷できた理由がわかりません。

編集:さらにコンテキストを追加しました。

4

3 に答える 3

1

さて、投稿されたコード スニペットが問題を説明するのに役立つように見えることから始めますが、全体像が描かれているとは思いません。完全なコンテキストを取得するために、もう少しコードが必要です。

そうは言っても、次のガイダンスを提供します。

  1. Java に組み込まれているオブジェクトのシリアル化に依存しないでください。素晴らしく使いやすいですが、実行時に非常に不安定でエラーが発生しやすい可能性があります。カスタム オブジェクトのシリアル化と逆シリアル化のスキームをお勧めします。

  2. 作成しているゲームの範囲によっては、NIO の方が適切な選択になる場合があります。通常の IO に固執する場合は、ソケット IO を処理するスレッドを適切に処理するために、堅実なスレッド マネージャーを用意してください。

..これ以上のコードは必要ありません。これが私が提供できる最大の機能です。

于 2012-03-16T01:08:20.183 に答える
1

のように見えますlobbyControlnull、ではありませんgameListgameListnull の場合、スタックの一番上はgotGameList()ではなくメソッドになりますgotObject()

スリープが問題を解決する場合は、lobbyControl適切な同時実行保護なしでメンバーを操作しているに違いありません。AnObjectInputStreamは、ストリームから完全に読み取られるまでオブジェクトを返さないため、オブジェクトを完全に読み取っていないこととは何の関係もありません。


更新:すべてのコードをたどることはできませんが、構築中のオブジェクトへの参照が、コンストラクターが完了する前に開始されるスレッド (client内の) にリークしているようです。NetControlもしそうなら、それは非常に悪いことです。部分的に構築されたオブジェクトを別のスレッドから見えるようにしないでください。

于 2012-03-16T01:17:28.283 に答える
0

私のコメントを改善するために... 1 つ以上のスレッドが終了するのを待つ必要があるときは、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 に答える