这样的代码是否可能不是线程安全的?它是一个静态方法,我们使用的是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();
}
答案 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
代替。