PhysicsThread.java 启动一个新线程。 run方法执行initialize方法。这种方法创造了物理世界。之后,它启动一个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;
}
}
答案 0 :(得分:3)
我怀疑这可能取决于你的构造技术,这是一个反模式。在构造函数中,不应让this
引用“转义”,因为其他线程可能会看到处于部分构造状态的对象。在这一点上,关于类的状态的所有投注都是关闭的,甚至总是为真的不变量(例如最终字段被设置为值)也可能不成立。
我想知道这里发生的事情是否可能是由于以下一系列事件造成的:
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
字段永远不会改变,为什么不执行{{1}的所有工作?在构造函数本身期间并将init
标记为dw
?这将更清晰,使代码更容易推理,因为VM保证在构造期间将完全设置值并且它永远不会更改如果你构建了很多这些实例并且从未真正运行它们,那么唯一的缺点就是性能损失 - 所以不要这样做。:)
此外,班级名称具有误导性,因为这不是final
而是Thread
。将其称为Runnable
或PhysicsTask
之类的内容可以说更清晰。)