今天遇到了org.jasig.cas.client.util.CommonUtils
的constructServiceUrl()
方法类(class)。我觉得他很奇怪:
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));
}
}
}
}
作者为什么要同步一个局部变量?
最佳答案
这是手动“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对你来说(但不是围绕循环结构,因为你最终可能会长时间持有锁)。
顺便说一句,编译器可以检测到对象没有从方法中“转义”,因此完全删除了对对象的获取和释放锁(锁省略),因为无论如何其他线程都无法访问该对象。在 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,带有外部同步块(synchronized block)的代码更快。但是,使用 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,带有外部同步块(synchronized block)的代码更快。但是,对于 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);
}
}
关于java - 局部变量同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6123024/