Java:StringBuilder,静态方法和可能的同步问题

时间:2009-10-08 13:32:54

标签: java string

这样的代码是否可能不是线程安全的?它是一个静态方法,我们使用的是stringbuilder的本地实例。我猜输入字符串可能被其他对象保存了吗?

public static String cat(final String ... strings) {

    ...
    ...
    final StringBuilder sb = new StringBuilder(totLength);
    for (int i = 0; i < size; i++) {        
        if (strings[i] != null) {
            sb.append(strings[i]);
        }
    }
    return sb.toString();
}

5 个答案:

答案 0 :(得分:11)

它不是完全线程安全的 - 因为另一个线程可能正在更改strings参数的参数中传递的相同数组。由于您使用了varargs,这并不完全是显而易见的,但有效地(就线程安全而言)方法签名只是:

public static String cat(String[] strings)

它不会导致任何异常,但您可能看不到数组中的最新值。

作为另一种选择,如果 看到更改,可能会看到意外的内容。例如,假设我们只传入一个单值数组,其值最初为“x”:

public static String cat(final String ... strings) {    
    ...
    ...
    final StringBuilder sb = new StringBuilder(totLength);
    for (int i = 0; i < size; i++) {
        // strings[0] is currently "x"
        if (strings[i] != null) {
            // But now another thread might change it to null!
            // At that point we'd get "null" as output
            sb.append(strings[i]);
        }
    }
    return sb.toString();
}

换句话说,虽然您可能期望看到“x”或“”作为结果,但您可以看到“null”。要解决此问题,您只需一次

即可读取数组中的每个值
final StringBuilder sb = new StringBuilder(totLength);
for (int i = 0; i < size; i++) {
    String value = strings[i];
    if (value != null) {
        sb.append(value);
    }
}

您可能仍会看到一个数组在更改的一半(例如,如果一个线程正在将{"x", "y"}更改为{"a", "b"},您可能会看到“xb”作为结果),但您不会得到一个假的“空”。

答案 1 :(得分:5)

这应该是线程安全的,因为传入的字符串是不可变的。假设您在方法中创建totLength,其他所有内容都是本地的。

编辑:

作为Jon Skeet points out,vargs值有可能不仅作为字符串序列(如我的答案所假设)传递,而且还作为String[]传递。在后一种情况下,有可能在处理时由另一个线程修改数组。

答案 2 :(得分:4)

不,它的编写(*)是线程安全的。字符串是不可变的,因此多个线程调用它并不重要。

(*代表您显示的代码)

答案 3 :(得分:3)

即使你没有传递不可变对象,这也是线程安全的,因为

  • 您没有改变任何输入参数
  • 您唯一要改变的仅限于方法的本地范围。

答案 4 :(得分:0)

请参阅John Skeet对阵列部分的回答。

至于StringBuilder,它应该是安全的。由于StringBuilder是在方法内创建的,因此每个调用都有自己的StringBuilder

StringBuilder本身未同步,因此如果它在静态方法调用之前存在,那么最好使用StringBuffer代替。