Java 如何在渲染线程和;更新程序线程?
我有一个复杂的对象,我们称之为Java 如何在渲染线程和;更新程序线程?,java,synchronized,volatile,Java,Synchronized,Volatile,我有一个复杂的对象,我们称之为World,它包含其他包含玩家数据的对象、描述世界地图的对象等。它用于基于回合的游戏,我在屏幕上渲染World,但使用单独的线程来更新World,因为这需要几秒钟的时间来运行。在渲染线程中运行这一切只会冻结屏幕 这是如何在非渲染线程中更新世界,并将其传递回渲染线程: // copy the world that is used for rendering World world; synchronized (Renderer.this.sync) { world
World
,它包含其他包含玩家数据的对象、描述世界地图的对象等。它用于基于回合的游戏,我在屏幕上渲染World
,但使用单独的线程来更新World
,因为这需要几秒钟的时间来运行。在渲染线程中运行这一切只会冻结屏幕
这是如何在非渲染线程中更新世界
,并将其传递回渲染线程:
// copy the world that is used for rendering
World world;
synchronized (Renderer.this.sync) {
world = Renderer.this.world;
}
World clone = Util.clone(world);
// update the world
Updater.update(clone);
// pass the updated world back to the render thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized (Renderer.this.sync) {
Renderer.this.world = clone;
}
}
});
以下是渲染的工作方式:
// member variable: our sync-object
public final Object sync = new Object();
// member variable for world
public World world;
public void render() {
... renders the world object...
}
以下是我的问题:
- 这是否正确地将新世界对象传回渲染线程?我很确定对新克隆世界的引用是正确的,但是克隆世界的内容是否也与世界对象同步李>
- 是否应使用“挥发性”以及如何使用
- 例如
是否正确同步world.getPlayer(index.getName()
不确定问题是否清楚?我会根据需要澄清的谢谢 我刚上了一门关于Spring框架的课程。讲师花了大量时间向我们证明,开发多线程应用程序是极其复杂的。即使您认为您将每一段敏感代码都包装在同步块中,您也可能会因为运行时优化(如分支预测等)而出现意外行为 volatile关键字用于控制这种不可预测的行为(即,对分支预测并行处理设置障碍)
我在回答中声明,我没有太多开发多线程应用程序的经验。话虽如此,我还是将World对象设计为一个单例对象,这样它就只有一个副本,一次只有一个线程可以访问它。我刚上了一门关于Spring框架的课程。讲师花了大量时间向我们证明,开发多线程应用程序是极其复杂的。即使您认为您将每一段敏感代码都包装在同步块中,您也可能会因为运行时优化(如分支预测等)而出现意外行为 volatile关键字用于控制这种不可预测的行为(即,对分支预测并行处理设置障碍)
我在回答中声明,我没有太多开发多线程应用程序的经验。话虽如此,我还是将World对象设计为一个单例对象,这样它就只有一个副本,一次只有一个线程可以访问它。回答第一个问题需要更多的澄清,因为代码不清楚。然而,对于你的第二个问题,我可以说你可以通过三种方式来做到:
synchronized
,就像您在这里所做的那样(除了您没有向我们显示渲染器代码,所以我不能确定这一点)volatile
原子参考
synchronized (Renderer.this.sync) {
world = Renderer.this.world;
}
World clone = Util.clone(world);
// update the world
Updater.update(clone);
// publish the reference back to the renderer
synchronized (Renderer.this.sync) {
Renderer.this.world = clone;
}
这一切都是在非渲染器线程中完成的。然后,在发布引用后,您应该以某种方式通知渲染器,使其将已发布的引用复制到局部变量(同样,在同步的
块中),然后对其进行渲染。这样可以确保它获得正确更新的世界,并且在渲染过程中不会热交换世界
引用
这基本上是关于两个操作:读取两个线程之间共享的引用和写入它。有一种东西叫做。适用于这种情况:
- 监视器上的解锁发生在监视器上的每个后续锁定之前李>
- 对易失性字段(§8.3.1.4)的写入发生在该字段的每次后续读取之前李>
发生在每个后续的AtomicReference.set
之前(根据,get和set与volatile读/写具有相同的语义)AtomicReference.get
volatile
字段或AtomicReference
(两者都比synchronized
提供更好的性能)执行完全相同的操作。只需使用volatile
赋值或调用设置/获取原子引用来替换那些同步的
赋值块
现在,我们讨论的是引用本身的写入/读取。世界
的字段或它引用的对象是什么?好的,在一个线程中发生的事情也会在关系之前发生,因为它们是按程序顺序出现的。因此,在将引用发布到克隆之前,无论您向世界
对象的字段写入什么内容(或属于其对象图的任何内容,或任何其他相关内容),渲染线程都可以看到:
clone.setSomeProperty(someValue); // this...
synchronized (Renderer.this.sync) {
Renderer.this.world = clone; // ...happens-before this, which in turn...
}
// ...
synchronized (Renderer.this.sync) {
world = Renderer.this.world; // ...happens-before this in the rendering thread
// (assuming the rendering thread entered this block after the
// updating thread entered its block, otherwise we'll just see
// the old world here and will pick up the new one on the next try)
}
如果使用传递给
SwingUtilities.invokeLater()
的匿名Runnable
等闭包将clone
变量直接传递给另一个线程,会发生什么情况?这取决于如何实现SwingUtilities.invokeLater()
。不幸的是,这些文件对这个问题并不十分清楚。我想这是安全的,但为了绝对肯定,我宁愿从当前线程安全地发布新引用(如上面的示例所示),或者依赖final
字段语义,如下所示:
SwingUtilities.invokeLater(new Runnable() {
private final World world = clone; // final semantics enforced
public void run() {
Renderer.this.world = world; // this runs after the Runnable is constructed
// and therefore is guaranteed to see the updated version of the world
}
});
您应该参考文档,了解用于在渲染器线程中执行代码的OpenGL函数。如果它说调用runInRenderThread()
发生在代码执行之前,那么您就安全了。