Ant*_*ton 35 java concurrency performance multithreading synchronization
我对synchronizedJava 中块的性能有一点争议.这是一个理论问题,不影响现实生活中的应用.
考虑单线程应用程序,它使用锁定和同步部分.这个代码是否比没有同步部分的相同代码更慢?如果是这样,为什么?我们不讨论并发性,因为它只是单线程应用程序
更新
找到有趣的 基准测试吧.但它是从2001年开始的.在最新版本的JDK中,情况可能会发生巨大变化
Edw*_*son 48
使用synchronized块时,单线程代码仍然会运行得更慢.显然,在等待其他线程完成时你不会让其他线程停滞,但是你将不得不处理同步的其他影响,即缓存一致性.
同步块不仅用于并发,还用于可见性.每个同步的块都是一个内存屏障:JVM可以自由地处理寄存器中的变量,而不是主内存,假设多个线程不会访问该变量.没有同步块,这些数据可以存储在CPU的缓存中,不同CPU上的不同线程不会看到相同的数据.通过使用同步块,可以强制JVM将此数据写入主内存,以便查看其他线程.
因此,即使您没有锁定争用,JVM仍然需要在将数据刷新到主内存时进行内务处理.
另外,这具有优化约束.JVM可以自由重新排序指令以提供优化:考虑一个简单的例子:
foo++;
bar++;
Run Code Online (Sandbox Code Playgroud)
与:
foo++;
synchronized(obj)
{
bar++;
}
Run Code Online (Sandbox Code Playgroud)
在第一个示例中,编译器可以自由加载foo,bar同时将它们两者递增,然后将它们保存.在第二个示例中,编译器必须执行load/add/save foo,然后执行load/add/save bar.因此,同步可能会影响JRE优化指令的能力.
(关于Java内存模型的优秀书籍是Brian Goetz的Java Concurrency In Practice.)
Ant*_*ton 35
HotSpot中有3种类型的锁定
默认情况下,JVM使用精简锁定.稍后如果JVM确定没有争用,则将瘦锁定转换为偏置锁定.更改锁类型的操作相当昂贵,因此JVM不会立即应用此优化.有一个特殊的JVM选项 - XX:BiasedLockingStartupDelay = delay,它告诉JVM何时应该应用这种优化.
一旦有偏差,该线程随后可以锁定和解锁对象,而无需借助昂贵的原子指令.
回答问题:这取决于.但是如果有偏见,带锁定和无锁定的单线程代码具有平均相同的性能.
NPE*_*NPE 20
获取无争议的锁会有一些开销,但在现代JVM上,它非常小.
与此案例相关的关键运行时优化称为"偏置锁定",并在Java SE 6性能白皮书中进行了解释.
如果您想要一些与JVM和硬件相关的性能数字,您可以构建一个微基准测试来测试这种开销.
在不需要时使用锁定会降低应用程序的速度.它可能太小而无法测量,或者可能会出乎意料地高.
恕我直言通常最好的方法是在单线程程序中使用无锁代码,以明确此代码不打算跨线程共享.对于维护而言,这可能比任何性能问题更重要.
public static void main(String... args) throws IOException {
for (int i = 0; i < 3; i++) {
perfTest(new Vector<Integer>());
perfTest(new ArrayList<Integer>());
}
}
private static void perfTest(List<Integer> objects) {
long start = System.nanoTime();
final int runs = 100000000;
for (int i = 0; i < runs; i += 20) {
// add items.
for (int j = 0; j < 20; j+=2)
objects.add(i);
// remove from the end.
while (!objects.isEmpty())
objects.remove(objects.size() - 1);
}
long time = System.nanoTime() - start;
System.out.printf("%s each add/remove took an average of %.1f ns%n", objects.getClass().getSimpleName(), (double) time/runs);
}
Run Code Online (Sandbox Code Playgroud)
版画
Vector each add/remove took an average of 38.9 ns
ArrayList each add/remove took an average of 6.4 ns
Vector each add/remove took an average of 10.5 ns
ArrayList each add/remove took an average of 6.2 ns
Vector each add/remove took an average of 10.4 ns
ArrayList each add/remove took an average of 5.7 ns
Run Code Online (Sandbox Code Playgroud)
从性能的角度来看,如果4 ns对您很重要,则必须使用非同步版本.
对于99%的用例,代码的清晰度比性能更重要.清晰,简单的代码通常也表现得相当不错.
BTW:我使用4.6 GHz i7 2600和Oracle Java 7u1.
为了进行比较,如果我执行以下操作,其中perfTest1,2,3是相同的.
perfTest1(new ArrayList<Integer>());
perfTest2(new Vector<Integer>());
perfTest3(Collections.synchronizedList(new ArrayList<Integer>()));
Run Code Online (Sandbox Code Playgroud)
我明白了
ArrayList each add/remove took an average of 2.6 ns
Vector each add/remove took an average of 7.5 ns
SynchronizedRandomAccessList each add/remove took an average of 8.9 ns
Run Code Online (Sandbox Code Playgroud)
如果我使用常用perfTest方法,它不能以最佳方式内联代码,并且它们都慢
ArrayList each add/remove took an average of 9.3 ns
Vector each add/remove took an average of 12.4 ns
SynchronizedRandomAccessList each add/remove took an average of 13.9 ns
Run Code Online (Sandbox Code Playgroud)
交换测试顺序
ArrayList each add/remove took an average of 3.0 ns
Vector each add/remove took an average of 39.7 ns
ArrayList each add/remove took an average of 2.0 ns
Vector each add/remove took an average of 4.6 ns
ArrayList each add/remove took an average of 2.3 ns
Vector each add/remove took an average of 4.5 ns
ArrayList each add/remove took an average of 2.3 ns
Vector each add/remove took an average of 4.4 ns
ArrayList each add/remove took an average of 2.4 ns
Vector each add/remove took an average of 4.6 ns
Run Code Online (Sandbox Code Playgroud)
一次一个
ArrayList each add/remove took an average of 3.0 ns
ArrayList each add/remove took an average of 3.0 ns
ArrayList each add/remove took an average of 2.3 ns
ArrayList each add/remove took an average of 2.2 ns
ArrayList each add/remove took an average of 2.4 ns
Run Code Online (Sandbox Code Playgroud)
和
Vector each add/remove took an average of 28.4 ns
Vector each add/remove took an average of 37.4 ns
Vector each add/remove took an average of 7.6 ns
Vector each add/remove took an average of 7.6 ns
Vector each add/remove took an average of 7.6 ns
Run Code Online (Sandbox Code Playgroud)