Java1.7:同步(this)比同步void快

Java1.7:同步(this)比同步void快,java,multithreading,optimization,Java,Multithreading,Optimization,我目前正在为N体问题实现Barnes-Hut算法的多线程版本。虽然算法有效,但它不是很优化,我正在努力减少程序的运行时间 我已经确保有几个线程可以准确地找到我正在使用的空间的边界,并且意识到我在其中设置边界的最高级别对象中的代码是相当未优化的。看起来是这样的: public synchronized void setBorders(float maxX, float minX, float maxY, float minY, int thread){ if(maxX > this.ma

我目前正在为N体问题实现Barnes-Hut算法的多线程版本。虽然算法有效,但它不是很优化,我正在努力减少程序的运行时间

我已经确保有几个线程可以准确地找到我正在使用的空间的边界,并且意识到我在其中设置边界的最高级别对象中的代码是相当未优化的。看起来是这样的:

public synchronized void setBorders(float maxX, float minX, float maxY, float minY, int thread){
  if(maxX > this.maxX){ 
     this.maxX = maxX;  
  }
  if(maxY > this.maxY){ 
     this.maxY = maxY;  
  }
  if(this.minX > minX){ 
     this.minX = minX;  
  }
  if(this.minY > minY){ 
   this.minY = minY;    
  }
}
我有几个线程试图在计算出各自的值后访问此方法。由于同步对象在给定时间只能由单个线程访问,因此可以显著改进这一点

我想到的一个可能的解决方案是删除“public synchronized void”,并将代码重写为:

public synchronized void setBorders(float maxX, float minX, float maxY, float minY, int thread){
  Synchronize(this){      
    if(maxX > this.maxX){ 
       this.maxX = maxX;    
    }
  }

  Synchronize(this){
    if(maxY > this.maxY){ 
       this.maxY = maxY;    
    }
  }

  Synchronize(this){
    if(this.minX > minX){ 
       this.minX = minX;    
    }
  }

  Synchronize(this){
    if(this.minY > minY){ 
     this.minY = minY;  
    }
  }
}
}
如果我对Synchronized块的理解是正确的,即在任何给定时间只有一个线程可以访问Synchronized(this)块中的代码,那么这应该会加快代码的速度

这行得通吗,还是我应该避免错过的原因


编辑:哇,我对你们提供的帮助的速度和准确性感到惊讶。我真的很感谢这一切

为了更有效地优化它,您可以将setboorders例程分成4个方法(每个参数1个),并使用4个锁定对象分别锁定每个setter方法。。。但是,只有当设置程序被单独调用(不总是作为块)和/或不总是以相同的顺序调用时,或者当您确定值实际上没有改变时,如果您可以从设置程序中快速转义,那么这将更加有效,因为代码应该执行得非常快,因此,我怀疑将其拆分为更多的同步块是否会减少争用。我会选择单一的、功能级别的同步

但是,如果代码做得更多,例如

...
if(maxX > this.maxX){ 
   this.maxX = maxX; 
   doSomeSlowerCalculation();
   updateSomeComplexSharedDataStructure();
}
...

然后将其拆分为单独的同步块可能有助于

有三个选项可以避免或减少同步:

使用原子学 使用Guava中的类,您可以

private final AtomicDouble maxX = new AtomicDouble(Double.MIN_VALUE);
而且很简单

while (true) {
     double currentMaxX = this.maxX.get();
     if (currentMaxX >= maxX) break;
     boolean ok = compareAndSet(currentMaxX, maxX);
     if (ok) break;
}
如果你真的要使用float,写你自己的类,几行就行了

没有同步,只是一个

双重检查锁定 与

和Java1.5或更高版本,下面就可以了

if (maxX > this.maxX) {
    synchronized (this) {
        if (maxX > this.maxX) {
            this.maxX = maxX;  
        }
    }
}
最小化共享
计算您的本地最大/最小值,仅在几次迭代后更新共享状态。这是最简单的,但可能不适用于您的用例。

首先,将synchronized添加到方法定义中,如

synchronized void foo() {
    ...
}
是同一件事吗

void foo() {
    synchronized(this) {
        ...
    }
}
如果像第二个示例那样嵌套同步块:

synchronized void foo() {
    synchronized(this) {
        ...
    }
    synchronized(this) {
        ...
    }
}
然后内部块对同步没有任何影响,调用方法的线程仍然必须在进入方法时获取对象的监视器,并在退出时释放它

如果从方法中删除已同步的,那么您所拥有的只是:

void foo() {
    synchronized(this) {
        ...
    }
    synchronized(this) {
        ...
    }
}
然后,当线程执行此代码时,它们必须分别获取每个块。因此,如果多个线程正在设置不同的变量,那么您可能会得到一个线程设置了一些字段,而另一个线程设置了其他字段的对象

对于较小的同步块,线程可能会花费更多时间争夺锁。对于这些块中的每一个,调度程序都必须决定下一个获得锁的线程。锁获取是不公平的,无法确定下一个线程将获得锁


不明显的是,较低粒度的方法会更快,最好让线程进入,设置所有字段,然后退出。第二个例子可能只是让调度器工作得更努力,没有什么好的理由。唯一确定的区别是,第二种方法将允许混合来自不同线程的数据

我在这里看到了几个选择。编译器可能会将后一个代码转换为前一个代码,因为同步块之间没有代码,或者由于更多的同步争用,性能会更差。我真的不知道这将如何提高性能。除非你的想法是使用4个不同的锁,而不是使用
这个
。为什么不进行测试并发布结果呢?几乎可以肯定,尽管JVM可以自由地组合不同的同步块,但速度是相同的。与几乎所有性能问题相同。。。第二个示例中方法声明上的synchronized修饰符看起来像是一个错误。很难看出让多个线程都争先恐后地设置单个对象的值有什么意义,如果一个线程设置了minX,而另一个线程设置了minY,你对结果满意吗?“获取和释放四个锁不应该比获取和释放一个锁快吗?”嗯,不。它应该需要四倍的时间。同步或它的影响最小化,但你真的认为,这是最昂贵的部分吗?我怀疑这一点,除非你在数百个内核上运行。使每个线程更多地同步(通常)不会使事情更快。@jtahlborn我理解你的意思。。。它就像不必要/无用的代码块,对吗?但在这种情况下,即使每个线程都以相同的方式工作,单独锁定变量将允许一次最多处理4个线程,而不仅仅是1个线程。。。在任何情况下,这取决于代码的运行方式。对于同步块,一次只有一个线程在“工作”。而且工作很琐碎(简单的任务)。所以你的线程会为了锁而互相争斗4倍。
void foo() {
    synchronized(this) {
        ...
    }
    synchronized(this) {
        ...
    }
}