I just realized that I need to synchronize a significant amount of data collection code in an aspect but performance is a real concern. If performance degrades too much my tool will be thrown out. I will be writing ints and longs individually and to various arrays, ArrayLists and Maps. There will be multiple threads of an application that will make function calls that will be picked up by my aspect. What kind of things should I look out for that will negatively affect performance? What code patterns are more efficient?
我刚刚意识到我需要在一个方面同步大量的数据收集代码,但性能是一个真正的问题。如果性能下降太多,我的工具将被抛弃。我将单独编写int和long,以及各种数组,ArrayLists和Maps。将有一个应用程序的多个线程将进行将由我的方面拾取的函数调用。我应该注意哪些事情会对性能产生负面影响?什么代码模式更有效?
In particular I have a method that calls many other data recording methods:
特别是我有一种方法可以调用许多其他数据记录方法:
void foo() {
bar();
woz();
...
}
The methods mostly do adding an incrementing of aspect fields
这些方法主要是添加方面字段的递增
void bar() {
f++; // f is a field of the aspect
for (int i = 0; i < ary.length; i++) {
// get some values from aspect point cut
if (some condiction) {
ary[i] += someValue; // ary a field of the aspect
}
}
}
Should I synchronize foo, or bar, woz and others individually, or should I move all the code in bar, woz, etc into foo and just synchronize it? Should I synchronize on this
, on a specifically created synchronization object:
我应该单独同步foo,bar,woz和其他人,还是应该将bar,woz等中的所有代码移动到foo中并同步它?我应该在具体创建的同步对象上进行同步:
private final Object syncObject = new Object();
(see this post), or on individual data elements within the methods:
(请参阅此文章),或方法中的各个数据元素:
ArrayList<Integer> a = new ArrayList<Integer>();
void bar() {
synchronize(a) {
// synchronized code
}
}
5 个解决方案
#1
Concurrency is extremely tricky. It's very easy to get it wrong, and very hard to get right. I wouldn't be too terribly worried about performance at this point. My first and foremost concern would be to get the concurrent code to work safely (no deadlocks or race conditions).
并发非常棘手。这很容易弄错,而且很难做对。在这一点上,我不会过于担心表现。我首要关注的是让并发代码安全地工作(没有死锁或竞争条件)。
But on the issue of performance: when in doubt, profile. It's hard to say just how different synchronization schemes will affect performance. It's even harder for us to give you suggestions. We'd need to see a lot more of your code and gain a much deeper understanding of what the application does to give you a truly useful answer. In contrast, profiling gives you hard evidence as to if one approach is slower than another. It can even help you identify where the slowdown is.
但在性能问题上:当有疑问时,简介。很难说同步方案将如何影响性能。我们更难给你建议。我们需要查看更多代码,并深入了解应用程序的功能,为您提供真正有用的答案。相比之下,分析为您提供了关于一种方法是否比另一种方法慢的确凿证据。它甚至可以帮助您确定减速的位置。
There are a lot of great profiling tools for Java these days. The Netbeans and Eclipse profilers are good.
如今,Java有很多很棒的分析工具。 Netbeans和Eclipse分析器很好。
Also, I'd recommend staying away from raw synchronization altogether. Try using some of the classes in the java.util.concurrency
package. They make writing concurrent code much easier, and much less error prone.
另外,我建议完全远离原始同步。尝试使用java.util.concurrency包中的一些类。它们使编写并发代码变得更加容易,而且更不容易出错。
Also, I recommend you read Java Concurrency in Practice by Brian Goetz, et al. It's very well written and covers a lot of ground.
另外,我建议你阅读Brian Goetz等人的Java Concurrency in Practice。它编写得很好,涵盖了很多方面。
#2
Rule of thumb is not to synchronize on this
- most of the times it is a performance hit - all methods are synchronized on one object.
经验法则不是在此同步 - 大多数时候它是性能命中 - 所有方法都在一个对象上同步。
Consider using locks - they'a very nice abstraction and many fine features like, trying to lock for a time period, and then giving up:
考虑使用锁 - 它们是非常好的抽象和许多优秀的功能,例如,试图锁定一段时间,然后放弃:
if(commandsLock.tryLock(100, TimeUnit.MILLISECONDS)){
try {
//Do something
}finally{
commandsLock.unlock();
}
}else{
//couldnt acquire lock for 100 ms
}
I second opinion on using java.util.concurrent
. I'd make two levls of synchronization
我对使用java.util.concurrent的第二意见。我会做两个同步的同步
- synchronize collection access (if it is needed)
- synchronize field access
同步集合访问(如果需要)
同步字段访问
Collection access
If your collection are read-only
ie no elements get removed-inserted (but elements may change) i would say that you should use synchronized collections (but this may be not needed...) and dont synchronize iterations:
如果你的集合是只读的,即没有元素被删除 - 插入(但元素可能会改变)我会说你应该使用同步集合(但这可能不需要......)并且不要同步迭代:
Read only:
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
}
}
and ary is instance obtained by Collections.synchronizedList
.
和ary是Collections.synchronizedList获取的实例。
Read-write
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
}
}
}
Or use some concurrent collections (like CopyOnWriteArrayList) which is inherentently therad safe.
或者使用一些并发集合(如CopyOnWriteArrayList),这些集合是安全的。
Main difference is that - in first read-only wersion any number of threads may iterate over this collections, and in second only one at a time may iterate. In both cases only one therad at a time should increment any given field.
主要区别在于 - 在第一个只读的版本中,任何数量的线程都可以遍历此集合,而在第二个中,每次只能迭代一个。在这两种情况下,一次只有一个therad应该增加任何给定的字段。
Field access
Synchronize incrementations on fields separately from synchronizing iterations.
与同步迭代分开地同步字段上的增量。
like:
Integer foo = ary.get(ii);
synchronized(foo){
foo++;
}
Get rid of synchronization
- Use concurrent collections (from
java.util.concurrent
- not from `Collections.synchronizedXXX', latter still need synchronizing on traversal). - Use
java.util.atomic
that enable you to atomically incrememt fields.
使用并发集合(来自java.util.concurrent - 不是来自`Collections.synchronizedXXX',后者仍然需要在遍历上进行同步)。
使用java.util.atomic可以使您以原子方式递增字段。
Something you should watch:
Java memory model - its a talk that gives very nice understanding on how synchronizations and data aligment in JAVA works.
Java内存模型 - 这是一个很好地理解JAVA中的同步和数据对齐方式的演讲。
#3
Upadte: since writing the below, I see you've updated the question slightly. Forgive my ignorance-- I have no idea what an "aspect" is-- but from the sample code you posted, you could also consider using atomics/concurrent collections (e.g. AtomicInteger, AtomicIntegerArray) or atomic field updaters. This could mean quite a re-factoring of your code, though. (In Java 5 on a dual-proc hyperthreading Xeon, the throughput of AtomicIntegerArray is significantly better than a synchronized array; sorry, I haven't got round to repeating the test on more procs/later JVM version yet-- note that performance of 'synchronized' has improved since then.)
Upadte:自从写下面的内容后,我看到你已经略微更新了这个问题。请原谅我的无知 - 我不知道“方面”是什么 - 但是从您发布的示例代码中,您还可以考虑使用原子/并发集合(例如AtomicInteger,AtomicIntegerArray)或原子字段更新器。不过,这可能意味着对代码进行重新分解。 (在Java 5的双进程超线程Xeon上,AtomicIntegerArray的吞吐量明显优于同步数组;对不起,我还没有完成对更多procs /更高版本JVM版本的重复测试 - 请注意从那以后,'synchronized'有所改善。)
Without more specific information or metrics about your particular program, the best you can do is just follow good program design. It's worth noting that the performance and optimisation of synchronization locks in the JVM has beed one of the areas (if not, the area) that has received most research and attention over the last few years. And so in the latest versions of JVM's, it ain't all that bad.
如果没有关于您的特定计划的更具体的信息或指标,您可以做的最好的就是遵循良好的计划设计。值得注意的是,JVM中同步锁的性能和优化已成为过去几年中获得最多研究和关注的领域之一(如果不是,该领域)。所以在最新版本的JVM中,它并不是那么糟糕。
So in general, I'd say synchronize minimally without "going mad". By 'minimally', I mean so that you hold on to the lock for as less time as possible, and so that only the parts that need to use that specific lock use that specific lock. But only if the change is easy to do and it's easy to prove that your program is still correct. For example, instead of doing this:
所以一般来说,我会说最低限度的同步而不会“疯狂”。通过'minimally',我的意思是让你在尽可能少的时间内保持锁定,以便只有需要使用该特定锁的部分才能使用该特定锁。但是,只有改变很容易,并且很容易证明您的程序仍然正确。例如,而不是这样做:
synchronized (a) {
doSomethingWith(a);
longMethodNothingToDoWithA();
doSomethingWith(a);
}
consider doing this if and only if your program will still be correct:
当且仅当您的程序仍然正确时,请考虑这样做:
synchronized (a) {
doSomethingWith(a);
}
longMethodNothingToDoWithA();
synchronized (a) {
doSomethingWith(a);
}
But remember, the odd simple field update with a lock held unnecessarily probably won't make much tangible difference, and could actually improve performance. Sometimes, holding a lock for a bit longer and doing less lock "housekeeping" can be beneficial. But the JVM can make some of those decisions, so you don't need to be tooo paranoid-- just do generally sensible things and you should be fine.
但请记住,带有不必要锁定的奇怪的简单字段更新可能不会产生太大的实际差异,并且实际上可以提高性能。有时,持有一个锁更长时间并减少锁定“管家”可能是有益的。但是JVM可以做出一些决定,所以你不需要过于偏执 - 只要做一般明智的事情,你应该没事。
In general, try and have a separate lock for each set of methods/accesses that together form an "independent process". Other than that, having a separate lock object can be a good way of encapsulating the lock within the class it's used by (i.e. preventing it from being used by outside callers in a way you didn't predict), but there's probably no performance difference per se from using one object to another as the lock (e.g. using the instance itself vs a private Object declared just to be a lock within that class as you suggest), provided the two objects would otherwise be used in exactly the same way.
通常,尝试为每组方法/访问设置单独的锁,这些方法/访问一起构成“独立进程”。除此之外,拥有一个单独的锁定对象可以是将锁封装在其使用的类中的好方法(即防止外部调用者以您未预测的方式使用它),但可能没有性能差异本身从使用一个对象到另一个对象作为锁(例如,使用实例本身与私有对象声明只是你建议的那个类中的锁),前提是这两个对象将以完全相同的方式使用。
#4
There should be a performance difference between a built-in language construct and a library, but experience has taught me not to guess when it comes to performance.
内置语言构造和库之间应该存在性能差异,但经验告诉我不要猜测性能是什么。
#5
If you compile the aspect into the application then you will have basically no performance hit, if you do it at runtime (load-type weaving) then you will see a performance hit.
如果您将方面编译到应用程序中,那么基本上没有性能损失,如果您在运行时(负载类型编织)执行此操作,那么您将看到性能损失。
If you have each aspect be perinstance then it may reduce the need for synchronization.
如果您将每个方面都设置为perinstance,那么它可能会减少同步的需要。
You should have as little synchronization as possible, for as short a time as possible, to reduce any problems.
您应该尽可能少地同步,以尽可能短的时间,以减少任何问题。
If possible you may want to share as little state as possible between threads, keeping as much local as possible, to reduce any deadlock problems.
如果可能的话,您可能希望在线程之间尽可能少地共享状态,尽可能保持本地,以减少任何死锁问题。
More information would lead to a better answer btw. :)
更多信息将导致更好的答案顺便说一句。 :)
#1
Concurrency is extremely tricky. It's very easy to get it wrong, and very hard to get right. I wouldn't be too terribly worried about performance at this point. My first and foremost concern would be to get the concurrent code to work safely (no deadlocks or race conditions).
并发非常棘手。这很容易弄错,而且很难做对。在这一点上,我不会过于担心表现。我首要关注的是让并发代码安全地工作(没有死锁或竞争条件)。
But on the issue of performance: when in doubt, profile. It's hard to say just how different synchronization schemes will affect performance. It's even harder for us to give you suggestions. We'd need to see a lot more of your code and gain a much deeper understanding of what the application does to give you a truly useful answer. In contrast, profiling gives you hard evidence as to if one approach is slower than another. It can even help you identify where the slowdown is.
但在性能问题上:当有疑问时,简介。很难说同步方案将如何影响性能。我们更难给你建议。我们需要查看更多代码,并深入了解应用程序的功能,为您提供真正有用的答案。相比之下,分析为您提供了关于一种方法是否比另一种方法慢的确凿证据。它甚至可以帮助您确定减速的位置。
There are a lot of great profiling tools for Java these days. The Netbeans and Eclipse profilers are good.
如今,Java有很多很棒的分析工具。 Netbeans和Eclipse分析器很好。
Also, I'd recommend staying away from raw synchronization altogether. Try using some of the classes in the java.util.concurrency
package. They make writing concurrent code much easier, and much less error prone.
另外,我建议完全远离原始同步。尝试使用java.util.concurrency包中的一些类。它们使编写并发代码变得更加容易,而且更不容易出错。
Also, I recommend you read Java Concurrency in Practice by Brian Goetz, et al. It's very well written and covers a lot of ground.
另外,我建议你阅读Brian Goetz等人的Java Concurrency in Practice。它编写得很好,涵盖了很多方面。
#2
Rule of thumb is not to synchronize on this
- most of the times it is a performance hit - all methods are synchronized on one object.
经验法则不是在此同步 - 大多数时候它是性能命中 - 所有方法都在一个对象上同步。
Consider using locks - they'a very nice abstraction and many fine features like, trying to lock for a time period, and then giving up:
考虑使用锁 - 它们是非常好的抽象和许多优秀的功能,例如,试图锁定一段时间,然后放弃:
if(commandsLock.tryLock(100, TimeUnit.MILLISECONDS)){
try {
//Do something
}finally{
commandsLock.unlock();
}
}else{
//couldnt acquire lock for 100 ms
}
I second opinion on using java.util.concurrent
. I'd make two levls of synchronization
我对使用java.util.concurrent的第二意见。我会做两个同步的同步
- synchronize collection access (if it is needed)
- synchronize field access
同步集合访问(如果需要)
同步字段访问
Collection access
If your collection are read-only
ie no elements get removed-inserted (but elements may change) i would say that you should use synchronized collections (but this may be not needed...) and dont synchronize iterations:
如果你的集合是只读的,即没有元素被删除 - 插入(但元素可能会改变)我会说你应该使用同步集合(但这可能不需要......)并且不要同步迭代:
Read only:
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
}
}
and ary is instance obtained by Collections.synchronizedList
.
和ary是Collections.synchronizedList获取的实例。
Read-write
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
}
}
}
Or use some concurrent collections (like CopyOnWriteArrayList) which is inherentently therad safe.
或者使用一些并发集合(如CopyOnWriteArrayList),这些集合是安全的。
Main difference is that - in first read-only wersion any number of threads may iterate over this collections, and in second only one at a time may iterate. In both cases only one therad at a time should increment any given field.
主要区别在于 - 在第一个只读的版本中,任何数量的线程都可以遍历此集合,而在第二个中,每次只能迭代一个。在这两种情况下,一次只有一个therad应该增加任何给定的字段。
Field access
Synchronize incrementations on fields separately from synchronizing iterations.
与同步迭代分开地同步字段上的增量。
like:
Integer foo = ary.get(ii);
synchronized(foo){
foo++;
}
Get rid of synchronization
- Use concurrent collections (from
java.util.concurrent
- not from `Collections.synchronizedXXX', latter still need synchronizing on traversal). - Use
java.util.atomic
that enable you to atomically incrememt fields.
使用并发集合(来自java.util.concurrent - 不是来自`Collections.synchronizedXXX',后者仍然需要在遍历上进行同步)。
使用java.util.atomic可以使您以原子方式递增字段。
Something you should watch:
Java memory model - its a talk that gives very nice understanding on how synchronizations and data aligment in JAVA works.
Java内存模型 - 这是一个很好地理解JAVA中的同步和数据对齐方式的演讲。
#3
Upadte: since writing the below, I see you've updated the question slightly. Forgive my ignorance-- I have no idea what an "aspect" is-- but from the sample code you posted, you could also consider using atomics/concurrent collections (e.g. AtomicInteger, AtomicIntegerArray) or atomic field updaters. This could mean quite a re-factoring of your code, though. (In Java 5 on a dual-proc hyperthreading Xeon, the throughput of AtomicIntegerArray is significantly better than a synchronized array; sorry, I haven't got round to repeating the test on more procs/later JVM version yet-- note that performance of 'synchronized' has improved since then.)
Upadte:自从写下面的内容后,我看到你已经略微更新了这个问题。请原谅我的无知 - 我不知道“方面”是什么 - 但是从您发布的示例代码中,您还可以考虑使用原子/并发集合(例如AtomicInteger,AtomicIntegerArray)或原子字段更新器。不过,这可能意味着对代码进行重新分解。 (在Java 5的双进程超线程Xeon上,AtomicIntegerArray的吞吐量明显优于同步数组;对不起,我还没有完成对更多procs /更高版本JVM版本的重复测试 - 请注意从那以后,'synchronized'有所改善。)
Without more specific information or metrics about your particular program, the best you can do is just follow good program design. It's worth noting that the performance and optimisation of synchronization locks in the JVM has beed one of the areas (if not, the area) that has received most research and attention over the last few years. And so in the latest versions of JVM's, it ain't all that bad.
如果没有关于您的特定计划的更具体的信息或指标,您可以做的最好的就是遵循良好的计划设计。值得注意的是,JVM中同步锁的性能和优化已成为过去几年中获得最多研究和关注的领域之一(如果不是,该领域)。所以在最新版本的JVM中,它并不是那么糟糕。
So in general, I'd say synchronize minimally without "going mad". By 'minimally', I mean so that you hold on to the lock for as less time as possible, and so that only the parts that need to use that specific lock use that specific lock. But only if the change is easy to do and it's easy to prove that your program is still correct. For example, instead of doing this:
所以一般来说,我会说最低限度的同步而不会“疯狂”。通过'minimally',我的意思是让你在尽可能少的时间内保持锁定,以便只有需要使用该特定锁的部分才能使用该特定锁。但是,只有改变很容易,并且很容易证明您的程序仍然正确。例如,而不是这样做:
synchronized (a) {
doSomethingWith(a);
longMethodNothingToDoWithA();
doSomethingWith(a);
}
consider doing this if and only if your program will still be correct:
当且仅当您的程序仍然正确时,请考虑这样做:
synchronized (a) {
doSomethingWith(a);
}
longMethodNothingToDoWithA();
synchronized (a) {
doSomethingWith(a);
}
But remember, the odd simple field update with a lock held unnecessarily probably won't make much tangible difference, and could actually improve performance. Sometimes, holding a lock for a bit longer and doing less lock "housekeeping" can be beneficial. But the JVM can make some of those decisions, so you don't need to be tooo paranoid-- just do generally sensible things and you should be fine.
但请记住,带有不必要锁定的奇怪的简单字段更新可能不会产生太大的实际差异,并且实际上可以提高性能。有时,持有一个锁更长时间并减少锁定“管家”可能是有益的。但是JVM可以做出一些决定,所以你不需要过于偏执 - 只要做一般明智的事情,你应该没事。
In general, try and have a separate lock for each set of methods/accesses that together form an "independent process". Other than that, having a separate lock object can be a good way of encapsulating the lock within the class it's used by (i.e. preventing it from being used by outside callers in a way you didn't predict), but there's probably no performance difference per se from using one object to another as the lock (e.g. using the instance itself vs a private Object declared just to be a lock within that class as you suggest), provided the two objects would otherwise be used in exactly the same way.
通常,尝试为每组方法/访问设置单独的锁,这些方法/访问一起构成“独立进程”。除此之外,拥有一个单独的锁定对象可以是将锁封装在其使用的类中的好方法(即防止外部调用者以您未预测的方式使用它),但可能没有性能差异本身从使用一个对象到另一个对象作为锁(例如,使用实例本身与私有对象声明只是你建议的那个类中的锁),前提是这两个对象将以完全相同的方式使用。
#4
There should be a performance difference between a built-in language construct and a library, but experience has taught me not to guess when it comes to performance.
内置语言构造和库之间应该存在性能差异,但经验告诉我不要猜测性能是什么。
#5
If you compile the aspect into the application then you will have basically no performance hit, if you do it at runtime (load-type weaving) then you will see a performance hit.
如果您将方面编译到应用程序中,那么基本上没有性能损失,如果您在运行时(负载类型编织)执行此操作,那么您将看到性能损失。
If you have each aspect be perinstance then it may reduce the need for synchronization.
如果您将每个方面都设置为perinstance,那么它可能会减少同步的需要。
You should have as little synchronization as possible, for as short a time as possible, to reduce any problems.
您应该尽可能少地同步,以尽可能短的时间,以减少任何问题。
If possible you may want to share as little state as possible between threads, keeping as much local as possible, to reduce any deadlock problems.
如果可能的话,您可能希望在线程之间尽可能少地共享状态,尽可能保持本地,以减少任何死锁问题。
More information would lead to a better answer btw. :)
更多信息将导致更好的答案顺便说一句。 :)