2012-03-07 112 views
2

PhysicsThread.java启动一个新线程。 run方法执行初始化方法。这种方法创建物理世界。之后,它开始一个while循环,以1000/60 = 16毫秒的间隔更新世界。然而,我在循环中得到了一个nullpointerexception,因为它并不总是知道世界,尽管我检查了这一点。 可以解释发生了什么?线程执行顺序的Java

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

奇怪的是,''dw' get在调用'loop'方法之前被初始化。哪条生产线生产NPE?堆栈跟踪的前几行可能会有所帮助。 – 2012-03-07 12:37:39

+0

你是否在说如果检查'dw'是否为空,你正在获得NPE?这应该是不可能的,除非其他线程在此期间将'dw'改为null,而我认为这不会发生。 – Tudor 2012-03-07 12:43:03

+2

除非'dw.stepSimulation()'在内部产生一个NPE,否则你不会显示你的真实代码,它不能在你的''loop''代码中。 – stryba 2012-03-07 12:44:24

回答

3

我怀疑这可能会下降到您的施工技术,这是一种反模式。在构造函数期间,您不应该让this引用“逃脱”,因为其他线程可能会看到处于部分构造状态的对象。在这一点上,所有的赌注都是关于这个阶级的状态,甚至总是真的不变量(例如最终字段被设置为一个值)可​​能不成立。

我不知道这里发生了什么可能是由于事件的顺序如下:

  • 的构造函数被调用,并开始根据this新的线程。
  • 在被抢占之前,新线程运行并完全执行init(设置dw)和loop的一半。
  • 主线程继续与构造,并设置dwnull作为现场初始化的一部分。
  • 产生的线程继续运行deltalastTime后,然后看到空值为dw

我有点hesistant说是正是的情况下,因为我本来期望的构造体运行之前的字段被初始化。然而,当对象仍在构建时访问字段似乎是一个非常糟糕的主意。

为了解决这个问题,我建议开始在构造一个线程,而是具有调用代码之后启动线程,例如:

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。调用它像PhysicsTaskPhysicsSimulationRun可能会更清楚。)

+0

'dw'不会在构造函数的末尾设置为'null'。设置完成后将字段重置为“空”会很奇怪。 – stryba 2012-03-07 12:56:52

+0

你还想要'最终线程t =新线程(pt)'我猜。 – stryba 2012-03-07 12:59:10

+1

谢谢你的回答!我尝试了你所说的,并且它有效,所以这很好。我不完全明白为什么我必须使用final关键字。感谢您的帮助:) – Gmfreaky 2012-03-07 14:33:42