在Java中并发访问同一对象的不同成员
我熟悉Java中围绕并发的许多机制和习惯用法。让我感到困惑的是一个简单的概念:同一对象的不同成员的并发访问 我有一组可以由两个线程访问的变量,在本例中涉及游戏引擎中的图形信息。我需要能够修改一个线程中对象的位置,并在另一个线程中读取它。解决此问题的标准方法是编写以下代码:在Java中并发访问同一对象的不同成员,java,android,multithreading,performance,Java,Android,Multithreading,Performance,我熟悉Java中围绕并发的许多机制和习惯用法。让我感到困惑的是一个简单的概念:同一对象的不同成员的并发访问 我有一组可以由两个线程访问的变量,在本例中涉及游戏引擎中的图形信息。我需要能够修改一个线程中对象的位置,并在另一个线程中读取它。解决此问题的标准方法是编写以下代码: private int xpos; private object xposAccess; public int getXpos() { int result; synchronized (xposAccess
private int xpos;
private object xposAccess;
public int getXpos() {
int result;
synchronized (xposAccess) {
result = xpos;
}
return result;
}
public void setXpos(int xpos) {
synchronized (xposAccess) {
this.xpos = xpos;
}
}
然而,我正在编写一个实时游戏引擎,而不是一个20个问题的应用程序。我需要快速工作,尤其是当我访问和修改它们时,就像我对图形资产的位置一样频繁。我想删除同步开销。更好的是,我想完全消除函数调用开销
private int xpos;
private int bufxpos;
...
public void finalize()
{
bufxpos = xpos;
...
}
通过使用锁,我可以让线程彼此等待,然后在对象未被访问或修改时调用finalize()。在这个快速缓冲步骤之后,两个线程都可以自由地对对象进行操作,一个线程修改/访问xpos,另一个线程访问bufxpos
我已经成功地使用了类似的方法,将信息复制到第二个对象中,每个线程作用于一个单独的对象。然而,在上面的代码中,两个成员仍然是同一个对象的一部分,当我的两个线程同时访问该对象时,甚至当对不同的成员进行操作时,也会发生一些有趣的事情。不可预测的行为、幻象图形对象、屏幕位置的随机错误等。为了验证这确实是一个并发问题,我在一个线程中运行了两个线程的代码,在该线程中,它可以完美地执行
我希望性能高于一切,我正在考虑将关键数据缓冲到单独的对象中。我的错误是由并发访问相同对象引起的吗?有更好的并发解决方案吗
编辑:如果你怀疑我对绩效的评价,我应该给你更多的背景。我的引擎是为Android编写的,我用它来绘制成百上千的图形资源。我有一个单线程解决方案可以工作,但自从实现多线程解决方案以来,我看到性能几乎翻了一番,尽管存在虚拟并发问题和偶尔出现的未捕获异常
编辑:感谢关于多线程性能的精彩讨论。最后,我能够通过在工作线程处于休眠状态时缓冲数据,然后允许它们在对象中各自操作自己的数据集来解决这个问题。我不认为我完全理解您的意思,但一般来说 有更好的并发解决方案吗 是的,有:
- 优先于内在的内置锁
- 考虑使用中提供的非阻塞结构以获得更好的性能
public final class ImmutableMessage {
private final int xPos;
// ... other fields with adhering the rules of immutability
public ImmutableObject(int xPos /* arguments */) { ... }
public int getXPos() { return xPos; }
}
然后在writer线程中的某个地方:
sharedObject.message = new ImmutableMessage(1);
ImmutableMessage message = sharedObject.message;
int xPos = message.getXPos();
读者线程:
sharedObject.message = new ImmutableMessage(1);
ImmutableMessage message = sharedObject.message;
int xPos = message.getXPos();
共享对象(为简单起见的公共字段):
我猜实时游戏引擎中的情况变化很快,最终可能会创建大量ImmutableMessage
对象,这些对象最终可能会降低性能,但可能会被此解决方案的非锁定性质所平衡
最后,如果你有一个小时的空闲时间来讨论这个主题,我认为这是值得一看的。如果你只处理单个的原语,比如
AtomicInteger
,它的操作非常好。它们是非阻塞的,您可以获得大量的原子性,并在需要时返回到阻塞锁
对于以原子方式设置访问变量或对象,您可以利用锁,回到传统的锁
但是,在代码中最简单的一步是使用synchronized
,但不是使用隐式this
对象,而是使用几个不同的成员对象,每个需要原子访问的成员分区一个:synchronized(partition_2){/*…*/}
,synchronized(partition_1){/*…*/}
等,其中有成员私有对象分区1;
,私有对象分区2;
等
但是,如果无法对成员进行分区,则每个操作必须获取多个锁。如果是这样,请使用前面链接的lock
对象,但请确保所有操作都以某种通用顺序获取它所需的锁,否则代码可能会死锁
更新:即使是volatile
对性能造成了不可接受的影响,也不可能真正提高性能。基本的根本问题是,互斥必然意味着与内存层次结构的实质好处进行权衡,即aches。最快的每处理器核心内存缓存无法保存正在同步的变量。处理器寄存器可以说是最快的“缓存”即使处理器足够复杂,可以保持最近缓存的一致性,但仍然无法在寄存器中保留值。希望这能帮助您看到它是性能的基本块,没有魔杖
就移动平台而言,出于电池寿命的考虑,该平台的设计刻意避免让任意应用程序以尽可能快的速度运行。让任何一个应用程序在几个小时内耗尽电池并不是优先事项
给出第一个因素,最好做的是重新设计你的应用程序,这样它就不需要太多的互斥——考虑不一致地跟踪X-POS,除非两个对象彼此接近,比如说在10x10个方框内。