Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 什么是;StringBuilder不是线程安全的”;什么意思?_Java_String_Multithreading_Stringbuilder_Stringbuffer - Fatal编程技术网

Java 什么是;StringBuilder不是线程安全的”;什么意思?

Java 什么是;StringBuilder不是线程安全的”;什么意思?,java,string,multithreading,stringbuilder,stringbuffer,Java,String,Multithreading,Stringbuilder,Stringbuffer,我读过一些关于Java编程语言中String和StringBuilder的利弊的文章。在其中一篇文章中,作者提到: StringBuilder不是线程安全的,因此在多线程中使用 StringBuffer 不幸的是,我无法理解这意味着什么。请您解释一下String、StringBuilder和StringBuffer之间的区别,特别是在“线程安全”方面 如果您能用代码示例进行描述,我将不胜感激。如果多个线程正在修改一个StringBuilder的同一实例,则结果可能是意外的,即某些修改可能会丢失。

我读过一些关于Java编程语言中
String
StringBuilder
的利弊的文章。在其中一篇文章中,作者提到:

StringBuilder不是线程安全的,因此在多线程中使用 StringBuffer

不幸的是,我无法理解这意味着什么。请您解释一下
String
StringBuilder
StringBuffer
之间的区别,特别是在“线程安全”方面


如果您能用代码示例进行描述,我将不胜感激。

如果多个线程正在修改一个
StringBuilder
的同一实例,则结果可能是意外的,即某些修改可能会丢失。这就是为什么在这种情况下应该使用StringBuffer。但是,如果每个线程
StringBuilder
实例只能由一个线程修改,则最好使用
StringBuilder
,因为这样效率更高(线程安全会带来性能代价)。

如果多个线程尝试更改StringBuilder对象值,则结果会很奇怪。参见下面的示例

private StringBuilder sb = new StringBuilder("1=2");

public void addProperty(String name, String value) {
    if (value != null && value.length() > 0) {
        if (sb.length() > 0) {
            sb.append(',');
        }
        sb.append(name).append('=').append(value);
    }
}
如果许多线程调用addProperty方法,那么结果将是奇怪的(不可预测的结果)


最后,当您调用sb.toString()时,结果将是不可预测的。例如,它可能会带来像
1=2,ac=d=b,e=f
这样的输出,但您的期望值是
1=2,a=b,c=d,e=f

因为StringBuilder不是同步的,而StringBuffer是同步的。在多线程环境中使用StringBuilder时,多个线程可以同时访问StringBuilder对象,并且无法预测它产生的输出,因此StringBuilder不是线程安全的


使用StringBuffer,我们可以克服线程安全的问题,其中StringBuffer是线程安全的,因为它是同步的,一次只有一个线程可以访问,因此可以预期和预测它产生的输出。

StringBuilder的线程安全问题是该方法调用
StringBuilder
不同步

考虑
StringBuilder.append(char)
方法的实现:

public StringBuilder append(boolean b) {
    super.append(b);
    return this;
}

// from the superclass
public AbstractStringBuilder append(char c) {
     int newCount = count + 1;
     if (newCount > value.length)
         expandCapacity(newCount);
     value[count++] = c;
     return this;
 }
现在假设有两个线程共享一个
StringBuilder
实例,并且两个线程都试图同时追加一个字符。假设它们都得到
值[count++]=c
语句,并且
count
1
。每一个将在
值[1]
处将其字符写入缓冲区,然后更新
计数。显然,那里只能存储一个字符。。。因此,另一个将丢失。此外,
count
的一个增量可能会丢失

更糟糕的是,
值[count++]=c语句也可能失败。原因是Java内存模型说,除非有适当的同步(一个“before”关系),否则不能保证第二个线程会看到第一个线程进行的内存更新。实际发生的情况取决于第一个线程的更新是否以及何时写入主内存


现在让我们看看
StringBuffer.append(char)

这里我们看到
append
方法是
synchronized
。这意味着两件事:

  • 两个线程不能同时在同一个
    StringBuffer
    对象上执行超类
    append
    方法。因此,第一种情况不可能发生

  • synchronize
    表示不同线程对
    StringBuffer.append的连续调用之间在
    之前发生
    。这意味着后一个线程一定会看到前一个线程中的更新


字符串
的大小写再次不同。如果我们检查代码,就会发现没有明显的同步。但这没关系,因为
字符串
对象实际上是不可变的;i、 在
String
API中没有任何方法会导致
String
对象状态的外部可观察变化。此外:

  • final
    实例变量和构造函数的特殊行为意味着所有线程都将看到任何
    字符串的正确初始状态

  • 字符串
    在幕后是可变的地方,
    hashCode()
    方法将正确工作,无论线程是否看到
    hash
    变量的最新更改


参考资料:

  • StringBuilder的源代码-
  • StringBuffer的源代码-
  • 字符串的源代码-

    • 方法内部的StringBuilder是安全的

      public void addProperty(String name, String value) {
          private StringBuilder sb = new StringBuilder("1=2");
      
              if (value != null && value.length() > 0) {
                  if (sb.length() > 0) {
                      sb.append(',');
              }
              sb.append(name).append('=').append(value);
          }
      }
      

      参考:这可能很有用:主要用例是按顺序构建字符串,格式良好,使用StringBuilder/StringBuffer作为局部变量,而不需要多线程共享。因此,在大多数情况下,StringBuilder是主要的选择。即使您使用了
      StringBuffer
      ,您仍然可以得到您提到的不可预知的结果。Thread2可能会生成2个附加,然后Thread3生成1个附加,然后Thread2生成2个附加等等。这取决于JVM使用的线程管理策略。为了避免这种情况,您必须使整个方法
      addProperty
      同步。
      StringBuffer
      的区别在于它不允许在
      append
      方法本身中有多个线程(
      append
      是同步的),因此会覆盖彼此的更改-请参见下面Stephen的回答。@AdamBurley感谢您的回答。但我不明白你为什么解释
      public synchronized StringBuffer append(char c) {
          super.append(c);  // calls the "AbstractStringBuilder.append" method above.
          return this;
      }
      
      public void addProperty(String name, String value) {
          private StringBuilder sb = new StringBuilder("1=2");
      
              if (value != null && value.length() > 0) {
                  if (sb.length() > 0) {
                      sb.append(',');
              }
              sb.append(name).append('=').append(value);
          }
      }