这是一个手动“
lock coarsening”的例子,可能已经做了,以提高性能。
考虑这两个片段:
StringBuffer b = new StringBuffer();
for(int i = 0 ; i < 100; i++){
b.append(i);
}
与:
StringBuffer b = new StringBuffer();
synchronized(b){
for(int i = 0 ; i < 100; i++){
b.append(i);
}
}
在第一种情况下,StringBuffer必须获取和释放锁定100次(因为append是一种同步方法),而在第二种情况下,锁定被获取并释放一次。这可以给你一个性能提升,可能是为什么作者做到了。在某些情况下,编译器可以为你执行这个lock coarsening(但不是在循环结构,因为你可能最终持有一个锁很长一段时间)。
顺便说一下,编译器可以检测到一个对象不是从一个方法“转义”,所以删除获取和释放对象的锁(锁定elision),因为没有其他线程可以访问该对象。在JDK7年已经做了很多工作。
更新:
我进行了两个快速测试:
1)无需预热:
在这个测试中,我没有运行这些方法几次来“热身”JVM。这意味着Java热点服务器编译器没有得到优化代码的机会。通过消除逃逸对象的锁。
JDK 1.4.2_19 1.5.0_21 1.6.0_21 1.7.0_06
WITH-SYNC (ms) 3172 1108 3822 2786
WITHOUT-SYNC (ms) 3660 801 509 763
STRINGBUILDER (ms) N/A 450 434 475
使用JDK 1.4,具有外部同步块的代码更快。然而,使用JDK 5及以上的代码没有外部同步赢。
2)带温度:
在该测试中,在计算时间之前,将方法运行几次。这样做使JVM可以通过执行转义分析来优化代码。
JDK 1.4.2_19 1.5.0_21 1.6.0_21 1.7.0_06
WITH-SYNC (ms) 3190 614 565 587
WITHOUT-SYNC (ms) 3593 779 563 610
STRINGBUILDER (ms) N/A 450 434 475
再次,使用JDK 1.4,具有外部同步块的代码更快。但是,使用JDK 5及更高版本,两种方法的性能同样良好。
这里是我的测试类(随意改善):
public class StringBufferTest {
public static void unsync() {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
public static void sync() {
StringBuffer buffer = new StringBuffer();
synchronized (buffer) {
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
}
public static void sb() {
StringBuilder buffer = new StringBuilder();
synchronized (buffer) {
for (int i = 0; i < 9999999; i++) {
buffer.append(i);
buffer.delete(0, buffer.length() - 1);
}
}
}
public static void main(String[] args) {
System.out.println(System.getProperty("java.version"));
// warm up
for(int i = 0 ; i < 10 ; i++){
unsync();
sync();
sb();
}
long start = System.currentTimeMillis();
unsync();
long end = System.currentTimeMillis();
long duration = end - start;
System.out.println("Unsync: " + duration);
start = System.currentTimeMillis();
sync();
end = System.currentTimeMillis();
duration = end - start;
System.out.println("sync: " + duration);
start = System.currentTimeMillis();
sb();
end = System.currentTimeMillis();
duration = end - start;
System.out.println("sb: " + duration);
}
}