2012-03-07 101 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