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

Ber*_*own 2 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();
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 11

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

public static String cat(String[] strings)
Run Code Online (Sandbox Code Playgroud)

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

作为另一种选择,如果它确实看到了变化,您可能会看到意外的事情.例如,假设我们只传入一个单值数组,其值最初为"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();
}
Run Code Online (Sandbox Code Playgroud)

换句话说,虽然您可能希望看到"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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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


akf*_*akf 5

这应该是线程安全的,因为传入的字符串是不可变的.假设你totLength在方法中创建,其他一切都是本地的.

编辑:

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