Сетевой код Java, выполняющийся до получения объекта - PullRequest
1 голос
/ 16 марта 2012

У меня есть игра 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);
}

«приложение» - это экземпляр, который обрабатывает все новые полученные объекты и обрабатывает их.

Из приложения ( 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);
    }

И, поспав некоторое время, все наконец заработало.

Любая идея, почему мне нужно спать нить, как это? Есть ли что-то, что я должен сделать по-другому? Я не уверен, почему мне удалось напечатать объект, пока он все еще считался нулевым.

Редактировать: добавлен дополнительный контекст.

Ответы [ 3 ]

1 голос
/ 16 марта 2012

Похоже, lobbyControl это null, а не gameList. Если бы gameList было нулевым, вершиной стека был бы метод gotGameList(), а не gotObject().

Если сон помогает решить проблему, значит, вы должны манипулировать элементом lobbyControl без надлежащих мер по обеспечению параллелизма. ObjectInputStream не вернет объект, пока он не будет полностью прочитан из потока, поэтому ваша проблема не имеет ничего общего с неполным чтением объекта.


Обновление: Я не могу следовать всему коду, но похоже, что ссылка на конструируемый объект просочилась в поток (client в NetControl), который началось до завершения конструктора. Если это так, это очень, очень плохо. Вы должны никогда позволить частично сконструированному объекту стать видимым для другого потока.

1 голос
/ 16 марта 2012

Хорошо, я начну с того, что опубликованные фрагменты кода, кажется, помогают проиллюстрировать проблему, но я не думаю, что полная картина нарисована. Я бы попросил немного больше кода, чтобы помочь получить полный контекст.

При этом я бы предложил следующее руководство:

  1. Не опирайтесь на сериализацию встроенных объектов Java. Это приятно и прост в использовании, но может быть очень нестабильным и подверженным ошибкам во время выполнения. Я бы предложил сериализацию и десериализацию пользовательских объектов схема.

  2. В зависимости от объема игры, которую вы делаете, NIO может быть выбор нетто. Если вы придерживаетесь регулярного ввода-вывода, то убедитесь, что вы иметь надежный менеджер потоков, чтобы правильно обрабатывать темы, связанные с сокетом IO.

.. без кода, это самое большее, что я могу предложить.

0 голосов
/ 16 марта 2012

Просто для улучшения моего комментария ... Когда мне нужно дождаться окончания одного или нескольких потоков, мне нравится использовать 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();
    }
 }

Это просто идея, надеюсь, это поможет.

...