【问题标题】:Java StringBuilder and Thread SafetyJava StringBuilder 和线程安全
【发布时间】:2010-10-12 12:16:37
【问题描述】:

我正在构建一个由多个部分组成的字符串,并希望使用StringBufferStringBuilder 来执行此操作。从 Java 5 文档中,我看到 StringBuilder 在可能的情况下是首选,但需要注意的是

StringBuilder 的实例对于多线程使用是不安全的。

从这个声明中,我了解到我不应该有一个由多个线程共享的 StringBuilder 实例。但是这个案例呢:

//Is this safe?
//foo() is called simultaneously by multiple threads
String foo(String a, String b) {
    return new StringBuilder(a).append(b).toString();
}

这里可能有多个线程同时在函数中,同时使用StringBuilder类(例如,静态变量的并发访问,如果有的话),但每个线程都有自己独立的StringBuilder 的实例。从文档中,我不能完全确定这是否算作多线程使用。

【问题讨论】:

  • 将 foo() 设为静态方法也可能有一些好处,因为它不涉及任何实例变量。
  • @Kip:取决于班级。但有时您可能想要执行多态操作,而将方法设为静态会阻碍这一点。
  • 使用 String.concat 会更快,但我认为这只是一个例子。

标签: java multithreading thread-safety stringbuilder


【解决方案1】:

这很好。局部变量只要不访问或改变实例或类变量,就不会出现线程安全问题。

【讨论】:

    【解决方案2】:

    是的,这是安全的,因为 StringBuilder 对象仅在本地使用(每个调用 foo() 的线程都会生成自己的 StringBuilder)。

    您还应该注意,您发布的代码实际上与由此生成的字节码相同:

    String foo(String a, String b) {
        return a + b;
    }
    

    【讨论】:

    • 好吧,不一样,它有一个额外的指令..编译器将+运算符转换为:new StringBuilder().append(a).append(b);
    • 哇!除非我尝试过,否则我不会相信。唯一的区别是这个答案中有额外的 .append 调用,每个参数一个。嗯。
    【解决方案3】:

    您拥有的代码是安全的。

    这段代码不是。

    public class Foo
    {
        // safe
        private final static StringBuilder builder;
    
        public static void foo()
        {
            // safe
            builder = new StringBuilder();
        }
    
        public static void foo(final String a)
        {
            // unsafe
            builder.append(a);
        }
    
        public synchronized void bar(final String a)
        {
            // safe
            builder.append(a);
        }
    }
    

    仅使用本地数据的本地变量没有线程安全问题。只有开始处理在类或实例方法/变量级别可见的数据时,您才会遇到线程安全问题。

    【讨论】:

      【解决方案4】:

      同意其他答案——只是一个注释。

      如果存在多个线程使用 StringBuffer 的情况,这可能是一个完全损坏的用例,因为这意味着单个字符串是以准随机顺序构建的,因此没有意义使 StringBuffer 线程安全。

      【讨论】:

      • 这就是 StringBuilder 的基本原理。大多数时候不需要同步。
      • 是的,这让你想知道为什么他们不只是重写 StringBuffer 而不是创建并行的 StringBuilder。为构建非确定性字符串的应用保持向后兼容性?
      • 如果您使用 StringBuilder 对多线程应用程序进行某种内存日志记录?不知道你为什么要这样做......
      • @Kip 这是我唯一能想到的,当我试图证明它的合理性时,它似乎是一个符合我原来陈述的破碎用例,所以我把它省略了:)跨度>
      • 这些列表中的每个对象都有内存开销。 StringBuffer 允许您减少内存使用,而不是更多同步调用。
      【解决方案5】:

      我不确定是否需要此代码,因为我猜 Java 会自动选择 StringBuilder。如果您没有性能问题,请使用 a + b。

      如果需要性能,请尝试:

      return new StringBuilder(
      a.length() + b.length()).append(a).append(b).toString();
      

      它正确地调整缓冲区的大小并防止 VM 调整它的大小并在途中创建要收集的垃圾。

      【讨论】:

        猜你喜欢
        • 2013-05-30
        • 1970-01-01
        • 2015-05-16
        • 2012-06-19
        • 2014-03-31
        • 1970-01-01
        • 2023-03-27
        • 1970-01-01
        • 2015-08-22
        相关资源
        最近更新 更多