Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/19.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_Android_Multithreading_Performance - Fatal编程技术网

在Java中并发访问同一对象的不同成员

在Java中并发访问同一对象的不同成员,java,android,multithreading,performance,Java,Android,Multithreading,Performance,我熟悉Java中围绕并发的许多机制和习惯用法。让我感到困惑的是一个简单的概念:同一对象的不同成员的并发访问 我有一组可以由两个线程访问的变量,在本例中涉及游戏引擎中的图形信息。我需要能够修改一个线程中对象的位置,并在另一个线程中读取它。解决此问题的标准方法是编写以下代码: private int xpos; private object xposAccess; public int getXpos() { int result; synchronized (xposAccess

我熟悉Java中围绕并发的许多机制和习惯用法。让我感到困惑的是一个简单的概念:同一对象的不同成员的并发访问

我有一组可以由两个线程访问的变量,在本例中涉及游戏引擎中的图形信息。我需要能够修改一个线程中对象的位置,并在另一个线程中读取它。解决此问题的标准方法是编写以下代码:

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个方框内。