Порядок выполнения потоков Java - PullRequest
2 голосов
/ 07 марта 2012

PhysicsThread.java запускает новый поток.Метод run выполняет метод инициализации.Этот метод создает мир физики.После этого он запускает цикл while, который обновляет мир с интервалом 1000/60 = 16 миллисекунд.Тем не менее, я получаю nullpointerexception в цикле, потому что он не всегда знает мир, хотя я проверяю это. Может объяснить, что происходит?

PhysicsThread.java

public class PhysicsThread implements Runnable {

long fps = 60;
DiscreteDynamicsWorld dw;
long lasttime = System.currentTimeMillis();;

static int maxPhysicsObjects = 50000;

PhysicsThread() {
    Thread t = new Thread(this);
    t.setPriority(Thread.MIN_PRIORITY);
    t.start();
}

@Override
public void run() {
    System.out.println("Start thread");
    init();
    System.out.println("Start threadloop");

    while(true) {
        loop();
    }
}

public void init() {
    BroadphaseInterface broadphase = new AxisSweep3_32(new Vector3f(0,0,0), new Vector3f(200000,200000,200000), maxPhysicsObjects);//new DbvtBroadphase();
    DefaultCollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration();
    CollisionDispatcher dispatcher = new CollisionDispatcher(
            collisionConfiguration);

    SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();

    dw = new DiscreteDynamicsWorld(dispatcher, broadphase,
            solver, collisionConfiguration);
    System.out.println("Made world");
    dw.setGravity(new Vector3f(0, 0, -10));
}

public void loop() {
    if (dw!=null) {
        float delta = System.currentTimeMillis()-lasttime;
        lasttime = System.currentTimeMillis();
        dw.stepSimulation(delta/1000f, 1, 1/60f);
    }
    try {
        Thread.sleep(1000/fps);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public DiscreteDynamicsWorld getWorld() {
    return dw;
}

}

1 Ответ

3 голосов
/ 07 марта 2012

Я подозреваю, что это может быть связано с вашей строительной техникой, которая является антипаттерном.Вы не должны позволять this ссылаться на «escape» во время конструктора, потому что тогда другие потоки могут видеть объект в частично сконструированном состоянии.На этом этапе все ставки отключены относительно состояния класса, и даже инварианты, которые всегда являются истинными (например, для конечных полей устанавливается значение), могут не сохраняться.

Интересно, что здесь происходит?может быть из-за следующей последовательности событий:

  • Вызывается конструктор и запускает новый поток на основе this.
  • Новый поток запускается и полностью выполняет init (установка dw) и половина loop, перед тем как быть прерванным.
  • Основной поток продолжает работу с конструктором и устанавливает dw в null как часть инициализации поля.
  • порожденный поток продолжается после обработки delta и lastTime, затем видит нулевое значение для dw.

Я немного не согласен с тем, чтобы сказать, что это точно случай, потому что я ожидал, что поля будут инициализированы до запуска тела конструктора.Однако кажется вероятным, что доступ к полям во время создания объекта является очень плохой идеей.

Чтобы исправить это, я бы предложил не запуск потока в конструкторе,но, скорее всего, после того, как вызывающий код запустит поток позже, например:

final PhysicsThread pt = new PhysicsThread();
final Thread t = new Thread(pt);
t.setPriority(Thread.MIN_PRIORITY);
t.start();

(Кроме того, идея иметь init методы, которые не являются конструкторами, обычно является плохой идеей - если dwполе никогда не меняется, почему бы не выполнить всю работу init во время самого конструктора и пометить dw как final? Это будет чище и облегчит ваш код, так как виртуальная машина гарантирует, что значение будет полностьюустанавливается во время построения и никогда не изменится после этого. Единственным недостатком является снижение производительности, если вы создаете много этих экземпляров и никогда не запускаете их - так что просто не делайте этого: :)

Плюс классимя вводит в заблуждение, так как это не Thread, а Runnable.Называть это что-то вроде PhysicsTask или PhysicsSimulationRun, возможно, было бы более понятным.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...