【问题标题】:Is StringBuilder threadsafe (using it with parallelStream)?StringBuilder 是线程安全的(与 parallelStream 一起使用)吗?
【发布时间】:2015-08-09 12:59:33
【问题描述】:

我的代码:

StringBuilder sb = new StringBuilder();

events.parallelStream().forEach(event -> {
    sb.append(event.toString());
    sb.append("\n");
});

我不关心最终结果中events.toString() 的顺序。但我关心events.toString() 会正确地出现一行接一行,当然不会混淆/弄乱。

在这方面parallelStream(而不是stream)安全吗?

【问题讨论】:

标签: java multithreading thread-safety java-8 stringbuilder


【解决方案1】:

不,不是。如its javadoc中所述:

可变的字符序列。此类提供与 StringBuffer 兼容的 API,但不保证同步

请改用StringBuffer

【讨论】:

  • 请注意,如果在这种情况下使用 StringBuffer,您将不太可能从并行化中受益。我很确定它会比顺序流更慢,因为实际上不会并行执行任何操作;除了一个之外的所有线程都只会在监视器上等待。
  • @TagirValeev 这不是真的。事件的 toString 方法将并行执行。
【解决方案2】:

更好的解决方案是使用

events.parallelStream().map(event -> event+"\n").collect(Collectors.joining());

或者(感谢@Holger):

events.parallelStream().map(Object::toString).collect(Collectors.joining("\n", "", "\n"));

一般避免使用forEach 作为流的终端操作。通常像collectreduce 这样的归约操作是更好的选择。

【讨论】:

  • 还是更好:events.parallelStream().map(Object::toString).collect(Collectors.joining("\n", "", "\n"));
  • 谢谢,已将您的版本添加到答案中。
  • 关于forEach,应该注意它是无序的,因此即使在 lambda 表达式中进行了适当的同步,它也不会产生所需的结果。 forEachOrdered 可以解决这个问题以及线程问题,但在大多数情况下比collect 慢。
  • @seinecle,不,不是问题。使用Event::toStringevent -> event.toString() 也可以。
  • @seinecle: 正确的术语是 overridden 并且,你知道,如果你调用((Object)event).toString(),你仍然会在Event的@987654334 @ 方法。这同样适用于Object::toString 或一般的方法引用;例如方法,它们将调用覆盖方法,如果有的话。
【解决方案3】:

不,它不是线程安全的。

这是旧 StringBuffer 和新 StringBuilder 之间的主要区别 - 前者的方法是同步的,而后者的方法不是。

这样做不是很有用,即使您改用StringBuffer - 线程必须互相等待才能写入StringBuffer

【讨论】:

  • 不要忘记,即使使用StringBuffer,当 lambda 表达式在没有额外同步的情况下并发执行时,在 lambda 表达式中进行的两个 append 调用也可能是任意交错的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-30
  • 2010-10-12
  • 1970-01-01
  • 2011-09-04
  • 2011-04-16
  • 1970-01-01
相关资源
最近更新 更多