Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
线程执行顺序Java_Java_Multithreading_Initialization - Fatal编程技术网

线程执行顺序Java

线程执行顺序Java,java,multithreading,initialization,Java,Multithreading,Initialization,java启动一个新线程。run方法执行initialize方法。这种方法创造了物理世界。之后,它启动一个while循环,以1000/60=16毫秒的间隔更新世界。然而,我在循环中得到了一个nullpointerexception,因为它并不总是知道世界,即使我检查了它可以解释发生了什么? physicstread.java public class PhysicsThread implements Runnable { long fps = 60; DiscreteDynamicsWorld

java启动一个新线程。run方法执行initialize方法。这种方法创造了物理世界。之后,它启动一个while循环,以1000/60=16毫秒的间隔更新世界。然而,我在循环中得到了一个
nullpointerexception
,因为它并不总是知道世界,即使我检查了它可以解释发生了什么?

physicstread.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;
}

}

我怀疑这可能是由于您的构造技术,这是一种反模式。您不应该让
this
在构造函数期间引用“escape”,因为其他线程可能会看到对象处于部分构造状态。在这一点上,所有关于类状态的赌注都是无效的,即使是始终为真的不变量(例如将final字段设置为值)也可能不成立

我想知道这里发生的事情是否是由于以下一系列事件造成的:

  • 构造函数被调用,并根据
    启动新线程
  • 新线程运行并完全执行
    init
    (设置
    dw
    )和
    循环的一半,然后被抢占
  • 主线程继续使用构造函数,并将
    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
?这将更干净,使代码更易于推理,因为e VM保证在构建过程中完全设置该值,并且该值在构建之后永远不会更改。唯一的缺点是,如果您构建了大量这样的实例,并且从未实际运行过它们,则会对性能造成影响—所以不要这样做。:)


另外,类名有误导性,因为这不是一个
线程,而是一个
可运行的
。将其称为
physicTask
physicSimulationRun
可能更清楚。)

奇怪的是,
dw
get在调用
循环
方法之前被初始化。哪条生产线生产NPE?stacktrace的前几行可能会有帮助。您是说您正在检查
dw
是否为null的if中获取NPE吗?这应该是不可能的,除非其他线程同时将
dw
更改为null,我认为这不会发生。除非
dw.stepSimulation()
在内部生成NPE,否则您不会显示真正的代码,它不能在上面的
循环
代码中。@stryba-
dw
如果在初始空检查后修改,则它可能为空。我们在这里讨论的是线程,以及
系统。currentTimeMillis
是一种可能需要转到外部总线的东西,可能会导致上下文切换(或者只是另一个线程同时执行大量CPU指令)。@AndrzejDoyle:我同意如果从另一个线程将dw设置为
null
,但正如我所说,OP提供的代码没有出现这种情况,因此我反对他没有显示他的真实代码。
dw
不会在构造函数末尾设置为
null
。设置后将字段重置为
null
会很奇怪。我猜您还需要执行
final Thread t=new Thread(pt)
。谢谢您的回答!我试过你说的话,而且很有效,所以这很好。我不完全明白为什么我要用最后一个关键字来做这个。感谢您的帮助:)@stryba感谢您指出示例代码中的打字错误,已修复。你是对的,字段重置会很奇怪-这就是为什么你在构建过程中不发布对
这个
的引用,因为如果你这样做了,很多不变量就会停止使用!(在这种特定情况下,您可能是对的,但是如果在构造函数末尾将
dw
显式设置为null,那么它在功能上是相同的,并且肯定容易出现上述问题。因此,优化编译器可以选择这样重写代码,因为这不会产生任何差异。)@很高兴它起了作用!此修复的重要部分是没有在构造函数中启动线程,这本应自行解决问题。将
final
用于不变的东西仅仅是一种好的做法,因为它使您的代码更容易推理。