本地变量同步的必要性

8
在JSON-java库(org.json.JSONArray)中,我发现了一个代码片段,其中包含一个方法本地变量周围的synchronized块。
public String toString(int indentFactor) throws JSONException {
    StringWriter sw = new StringWriter();
    synchronized (sw.getBuffer()) {
        return this.write(sw, indentFactor, 0).toString();
    }
}

我不理解这里同步的必要性,因为 StringWriter 只在给定方法中是本地的(并且,为什么同步是在缓冲区上)。这里真的需要同步吗?如果需要,为什么?

8
不必要。使用默认的构造函数,缓冲区将是一个新实例化的对象,在构造函数内部有buf = new StringBuffer(); - Sotirios Delimanolis
8
请注意,org.json库的作者是一位JavaScript程序员而不是Java程序员,这一事实在代码中得到了体现。对于严肃的JSON处理,请考虑使用谷歌的Gson库。 - C. K. Young
4个回答

8

这可能是一种性能优化。在Oracle JVM中,重新获取已持有的锁非常快。据推测,write调用正在对StringBuffer进行多次调用。通过在调用write之前加锁,锁将在所有这些调用期间保持不变,而不是在每次调用时释放并重新获取。


从Java 6开始,锁偏向意味着获取无竞争锁也很快。 - C. K. Young
@ChrisJester-Young - 是的,我不知道那个特定的性能优化还有多少相关性(或曾经有多少相关性)。 - jtahlborn
我不理解每次调用都被释放和重新获取的含义。write() 调用根本没有在 Writer 上尝试进行同步。锁只在 OP 问题中的 toString(int) 方法内部被获取和释放一次。 - Sotirios Delimanolis
1
@SotiriosDelimanolis - StringBuffer的所有方法(该类是StringWriter的基础)都是同步的。 - jtahlborn

6
StringWriter的空构造函数为:
/**
 * Create a new string writer using the default initial string-buffer
 * size.
 */
public StringWriter() {
    buf = new StringBuffer();
    lock = buf;
}

没有共享的内容,因此synchronized块是不必要的。

除非...write委派给另一个线程,但我严重怀疑这一点。


1

getBuffer() 返回一个 StringBuffer,根据文档,StringBuffer 已经是同步的:

字符串缓冲区对于多个线程来说是安全的。必要时,方法会进行同步,以便所有在任何特定实例上的操作都表现为按照每个涉及的单个线程所做的方法调用顺序一致的某些串行顺序。

这意味着在 StringBuffer 上再次进行同步是完全多余的。因为 StringWriter 使用同步的 StringBuffer 内部,所以对 StringWriter 进行更改将自动同步。

由于 StringWriter 实例是局部于方法调用的,因此不可能有多个线程同时访问同一实例,这也使得同步变得不必要。


0

这是一个bug。 每个线程在方法中创建自己的本地变量并对其进行同步。每次进入方法时,线程都会创建自己的对象监视器,因为它是本地的并且仅存在于线程的堆栈上,所以其他线程无法持有它!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接