在一个方面上,Java的同步和性能

13 浏览
0 Comments

在一个方面上,Java的同步和性能

我刚意识到我需要在一个切面中同步大量的数据收集代码,但性能是一个真正的问题。如果性能下降太多,我的工具就会被淘汰。我将分别向各种数组、ArrayList和Map中写入int和long类型的数据。应用程序将有多个线程进行函数调用,这些调用将被我的切面捕捉到。我应该注意哪些会对性能产生负面影响的事情?哪些代码模式更高效?

特别是我有一个调用许多其他数据记录方法的方法:

void foo() {
    bar();
    woz();
    ...
}

这些方法主要是对切面字段进行累加和递增操作

void bar() {
    f++; // f是切面的一个字段
    for (int i = 0; i < ary.length; i++) {
        // 从切面切入点获取一些值
        if (某个条件) {
            ary[i] += someValue; // ary是切面的一个字段
        }
     }
 }

我应该同步foo方法,还是分别同步bar、woz和其他方法,或者将bar、woz等的所有代码移入foo并进行同步?我应该使用this同步,还是创建一个特定的同步对象:

private final Object syncObject = new Object();

(参考这篇帖子),或者在方法中的各个数据元素上进行同步:

ArrayList a = new ArrayList();
void bar() {    
    synchronize(a) {
        // 同步代码
    }
}

0
0 Comments

Java中的同步和性能问题是一种方面的问题。在没有更多具体信息或关于特定程序的度量标准的情况下,你只能遵循良好的程序设计。值得注意的是,JVM中同步锁的性能和优化是过去几年中接受最多研究和关注的领域之一。因此,在最新版本的JVM中,它并不那么糟糕。

因此,总体上来说,我会尽量“最少地同步而不要疯狂”。所谓的“最少地”,是指尽量在最短的时间内持有锁,并且只有需要使用特定锁的部分才使用特定锁。但前提是改变很容易并且很容易证明程序仍然正确。例如,不要这样做:

synchronized (a) {
  doSomethingWith(a);
  longMethodNothingToDoWithA();
  doSomethingWith(a);
}

而是要考虑这样做,前提是程序仍然正确:

synchronized (a) {
  doSomethingWith(a);
}
longMethodNothingToDoWithA();
synchronized (a) {
  doSomethingWith(a);
}

但要记住,不必要地持有锁的奇怪的简单字段更新可能不会有太大的实际差异,而且实际上可能会提高性能。有时,稍微长时间地持有锁并少做一些锁的“清理工作”可能是有益的。但是JVM可以做出其中一些决策,所以你不需要太过担心,只要做一些通常合理的事情,你应该没问题。

尽量为每个组成“独立过程”的方法/访问设置一个单独的锁。除此之外,使用单独的锁对象可以很好地封装在它所使用的类中(即防止它以未预测的方式被外部调用者使用),但从性能上讲,使用一个对象作为锁与使用另一个对象作为锁(例如使用实例本身与在该类中声明的一个私有对象作为锁,如你所建议的)可能没有本质的区别,只要这两个对象在使用方式上完全相同。

关于“方面”的概念,它是指当其他正在运行的代码中出现某些条件时调用的代码。详见eclipse.org/aspectj

谢谢,我会查一下。我已经模糊地听说过AspectJ了,现在你提到它,它看起来可能会成为我“无用框架”的另一个条目(像“横切”和“模块化”这样的词通常是警告信号),但至少我会为了“一般知识”的缘故而学习一下。

0
0 Comments

Java中同步和性能的一个方面

在Java中,同步是用来保护共享资源的,但过度的同步可能会导致性能下降。一个常见的经验法则是不要在this上同步,因为这样所有的方法都会在同一个对象上同步,从而导致性能下降。

解决方法之一是使用锁,锁提供了很好的抽象和许多有用的特性,比如尝试在一段时间内获取锁,然后放弃:

if(commandsLock.tryLock(100, TimeUnit.MILLISECONDS)){
     try { 
         //Do something
     }finally{
          commandsLock.unlock();
     }
}else{
     //couldnt acquire lock for 100 ms
}   

另一个建议是使用java.util.concurrent,可以对同步进行两个层次的处理:

1. 同步集合的访问(如果需要的话)

2. 同步字段的访问

对于只读集合,可以使用同步集合来保证线程安全,但迭代时不需要同步:

只读:

for (int i = 0; i < ary.length; i++) {
    // get some values from aspect point cut
    if (some condiction) {
        ary += someValue; // ary a field of the aspect
    }
 }

其中ary是通过Collections.synchronizedList获得的实例。

对于读写集合,可以使用synchronized关键字来同步集合的访问:

读写:

synchronized(ary){
    for (int i = 0; i < ary.length; i++) {
        // get some values from aspect point cut
        if (some condiction) {
            ary += someValue; // ary a field of the aspect
        }
     }
 }

或者使用一些并发集合(如CopyOnWriteArrayList),它们本质上是线程安全的。

字段的访问可以使用独立的同步方式,例如:

  Integer foo = ary.get(ii); 
  synchronized(foo){
      foo++;
  }

另外,可以通过以下方法来避免同步:

1. 使用并发集合(来自java.util.concurrent,而不是Collections.synchronizedXXX,后者仍然需要在遍历时进行同步)。

2. 使用java.util.atomic可以原子地增加字段的值。

最后,可以观看Java内存模型的讲座,这会帮助理解Java中同步和数据对齐的工作原理。

0
0 Comments

Java synchronization and performance in an aspect

在面向方面的编程中,Java同步和性能是一个方面问题。并发编程非常棘手,很容易出错,而且很难做到正确。目前,性能并不是最主要的问题,最重要的是确保并发代码能够安全地工作,避免出现死锁和竞态条件。

但是对于性能问题,当有疑问时,应该进行性能分析。很难说不同的同步方案会如何影响性能。我们更需要看到更多的代码并对应用程序有更深入的理解,才能给出真正有用的建议。相比之下,性能分析可以给出明确的证据,判断一个方法是否比另一个方法慢,并且还可以帮助你找出具体的性能瓶颈所在。

目前有很多优秀的Java性能分析工具。Netbeans和Eclipse提供的分析工具都很好用。

此外,我建议完全避免使用原始的同步机制,而是尝试使用java.util.concurrency包中的一些类。它们能够使并发编程更加容易,也更少出错。

我还推荐阅读Brian Goetz等人所著的《Java并发编程实战》。这本书写得非常好,涵盖了很多内容。

非常感谢你的反馈。不幸的是,我不能提供更多关于代码或周围系统的细节。当我说我不能提供更多细节时,你可能不会感到意外。不过,我会在得到结果后发布我的结果。

好吧,我说得太早了,我可以提供代码的更多细节。这是一个方面问题,方法主要是对方面的字段进行递增和添加操作。

0