问题描述
今天我遇到了课程。我以为他很奇怪:
Today I was faced with the method constructServiceUrl()
of the org.jasig.cas.client.util.CommonUtils
class. I thought he was very strange:
final StringBuffer buffer = new StringBuffer();
synchronized (buffer)
{
if (!serverName.startsWith("https://") && !serverName.startsWith("http://"))
{
buffer.append(request.isSecure() ? "https://" : "http://");
}
buffer.append(serverName);
buffer.append(request.getRequestURI());
if (CommonUtils.isNotBlank(request.getQueryString()))
{
final int location = request.getQueryString().indexOf(
artifactParameterName + "=");
if (location == 0)
{
final String returnValue = encode ? response.encodeURL(buffer.toString()) : buffer.toString();
if (LOG.isDebugEnabled())
{
LOG.debug("serviceUrl generated: " + returnValue);
}
return returnValue;
}
buffer.append("?");
if (location == -1)
{
buffer.append(request.getQueryString());
}
else if (location > 0)
{
final int actualLocation = request.getQueryString()
.indexOf("&" + artifactParameterName + "=");
if (actualLocation == -1)
{
buffer.append(request.getQueryString());
}
else if (actualLocation > 0)
{
buffer.append(request.getQueryString().substring(0, actualLocation));
}
}
}
}
为什么作者是否同步了一个局部变量?
Why did the author synchronizes a local variable?
推荐答案
这是手册的一个例子并且可能已经完成以获得性能提升。
This is an example of manual "lock coarsening" and may have been done to get a performance boost.
考虑以下两个片段:
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次(因为追加
是同步方法),而在第二种情况下,锁定只获取并释放一次。这可以为您提供性能提升,这可能是作者为什么这么做的原因。在某些情况下,编译器可以执行此 for you(但不是在循环结构周围,因为你可能会长时间持有一个锁)。
In the first case, the StringBuffer must acquire and release a lock 100 times (because append
is a synchronized method), whereas in the second case, the lock is acquired and released only once. This can give you a performance boost and is probably why the author did it. In some cases, the compiler can perform this lock coarsening for you (but not around looping constructs because you could end up holding a lock for long periods of time).
顺便说一句,编译器可以检测到对象不是从一个方法转义,因此完全删除获取和释放对象上的锁(锁定省略),因为无论如何没有其他线程可以访问该对象。在。
By the way, the compiler can detect that an object is not "escaping" from a method and so remove acquiring and releasing locks on the object altogether (lock elision) since no other thread can access the object anyway. A lot of work has been done on this in JDK7.
更新:
我进行了两次快速测试:
I carried out two quick tests:
1)没有热身:
在这个测试中,我没有运行几次方法来预热JVM。这意味着Java Hotspot Server Compiler没有机会优化代码,例如通过消除转义对象的锁定。
In this test, I did not run the methods a few times to "warm-up" the JVM. This means that the Java Hotspot Server Compiler did not get a chance to optimize code e.g. by eliminating locks for escaping objects.
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及更高版本,没有外部同步的代码将获胜。
With JDK 1.4, the code with the external synchronized block is faster. However, with JDK 5 and above the code without external synchronization wins.
2)WITH WARM-UP:
在此测试中,方法在计算时间之前运行了几次。这样做是为了让JVM可以通过执行转义分析来优化代码。
In this test, the methods were run a few times before the timings were calculated. This was done so that the JVM could optimize code by performing escape analysis.
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及更高版本,这两种方法的表现同样出色。
Once again, with JDK 1.4, the code with the external synchronized block is faster. However, with JDK 5 and above, both methods perform equally well.
这是我的测试类(随意改进):
Here is my test class (feel free to improve):
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);
}
}
这篇关于对局部变量进行同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!