Java: StringBuilder, статический метод и возможные проблемы с синхронизацией - PullRequest
2 голосов
/ 08 октября 2009

Возможно ли, чтобы такой код не был потокобезопасным? Это статический метод, и мы используем локальный экземпляр 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 ]

11 голосов
/ 08 октября 2009

Это не полностью потокобезопасно - потому что другой поток может изменять тот же массив, который был передан в аргументе для параметра 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» в результате), но вы не получите поддельные » нуль».

5 голосов
/ 08 октября 2009

Это должно быть Thread Safe, поскольку передаваемые строки являются неизменяемыми. и при условии, что вы создаете totLength в методе, все остальное является локальным.

EDIT:

как Джон Скит указывает , есть вероятность, что значение vargs может быть передано не только как последовательность строк (как предполагает мой ответ), но и как String[]. В последнем случае существует возможность изменения массива другим потоком во время обработки.

4 голосов
/ 08 октября 2009

Нет, это потокобезопасно, как написано (*). Строки являются неизменяемыми, поэтому не имеет значения, если несколько потоков вызывают это.

(* для кода, который вы показываете)

3 голосов
/ 08 октября 2009

Это также будет поточно-ориентированным, даже если вы не передавали неизменные объекты, потому что

  • вы не изменяете ни один из входных параметров
  • единственное, что вы изменяете, ограничено локальной областью применения метода.
0 голосов
/ 08 октября 2009

См. Ответ Джона Скита для части массива.

Что касается StringBuilder, то оно должно быть безопасным. Поскольку StringBuilder создается внутри метода, каждый вызов имеет свой собственный StringBuilder.

StringBuilder само не синхронизировано, поэтому, если бы оно существовало до статического вызова метода, было бы лучше использовать вместо него StringBuffer.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...