通过以下问题同步块可以比原子更快吗?,
我编写了一个简单的程序来比较AtomicInteger和synchronized块的性能差异(在其中增加int)。每次我运行这个程序,它给我的比率都大于100。我的笔记本电脑是Intel Corei3,下面的行打印为4。
System.out.println(Runtime.getRuntime().availableProcessors())
当我使用
THREAD_COUNT = 1;
比例最小。在100左右变化。
THREAD_COUNT = 10;
比值约为800。
问题1:请问这是测试AtomicInteger与synchronized increment()方法性能差异的正确方法吗?
问题2:如果我增加THREAD_COUNT
,比率增加,为什么?我认为这是因为更多的线程在同步语句中被阻塞,需要更多的CPU任务。请发表评论。
package concurrent.atomic;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class Performance {
private static final AtomicInteger atomicInt = new AtomicInteger(0);
private static volatile int counter = 0;
private static final Object LOCK = new Object();
private static final int THREAD_COUNT = 10;
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
Runnable atomic = new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
int value = atomicInt.incrementAndGet();
//if (value % 1000 == 0)
//System.out.println("atomic value : "+value);
}
}
};
//System.out.println("1");
Runnable intCounter = new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
synchronized (LOCK) {
int value = ++counter;
//if (value % 1000 == 0)
//System.out.println("sync value "+value);
}
}
}
};
final ExecutorService atomicExecutor = Executors.newCachedThreadPool();
final ExecutorService primitiveExecutor = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_COUNT ; ++i) {
atomicExecutor.submit(atomic);
primitiveExecutor.submit(intCounter);
}
while (true) {
float ratio = (((float) (atomicInt.get() * 1.0) ) / counter) * 100;
System.out.println("ratio : " + ratio);
}
}
}
问题1:你能告诉我这是测试AtomicInteger和synchronized increment()方法性能差异的正确方法吗?
你的测试代码有一些微妙的问题:
- 如果在
LOCK
上同步,那么counter
不应该是volatile
。否则,synchronized
代码将为锁和支付volatile
。 - 如果
counter
不是volatile
,那么在读取counter
的值时,需要同步LOCK
。 - 你不应该在主线程中旋转。这将极大地影响你的性能测试。至少在你的
while
循环中放一个Thread.sleep(100);
。 - 原来
Thread.currentThread().isInterrupted()
在我的Mac上很贵。不知道为什么。切换到while(true)
改变了数字。
有了这个,你就有了一个更好的测试,尽管这种孤立的测试并没有真正告诉我们除了你正在测试的东西之外的任何东西。在一行中执行一百万个中断与将20个中断混合到实际代码中是非常不同的。
问题2:如果我增加THREAD_COUNT,比率增加,为什么?我认为这是因为更多的线程在同步语句中被阻塞,需要更多的CPU任务。请发表评论。
AtomicInteger.incrementAndGet()
通过旋转工作,直到测试和设置成功。这与锁的获取、增加和释放非常不同。如果不知道精确的操作系统线程处理细节,很难解释这里发生的事情,我不确定这在所有情况下都是正确的。
在我的测试中,当线程数量越来越多地超过我的处理器数量时,这个比率往往会出现相当大的波动。随着更多的线程被添加,synchronize
处理显然受到了并发性增加的影响,尽管我不确定我还能从中得出什么。