【问题标题】:String allocation of literals文字的字符串分配
【发布时间】:2013-03-26 15:20:13
【问题描述】:

另一个question 回答了我如何在编译时评估字符串文字的连接。在我正在处理的一个项目中,我们使用StringBuffer 处理大查询的多行字符串。它只附加文字,所以它让我思考是否会发生类似的事情。

在以下代码中,缓冲区是否会在编译时附加其内容?当多个线程试图执行这个函数时,它会如何表现?

 public static String querySomething(int arg){


        StringBuffer buffer = new StringBuffer();
        buffer.append("A quite long query");
        buffer.append("that doesn't fit in one line");
        buffer.append("...");

  }

String 定义为常量不是更好吗,因为它是线程安全的,而且我们知道它可以在编译时与加号运算符连接起来。比如:

  private final static REALLY_LONG_QUERY1 = "A quite long query that"
                                            +"doesn't fit in one line"
                                            +"...";

【问题讨论】:

  • 由于StringBuffer被声明在在方法内,如果这个方法被多个线程同时调用,它不会被考虑在内。您甚至可以将其替换为 StringBuilder 并具有相同的行为(开销更少)。

标签: java string multithreading memory-management


【解决方案1】:

当您想要构建一个在编译时不知道实际大小的字符串时,StringBuffer 更适合,例如:

public static String querySomething(int arg) {
    StringBuffer buffer = new StringBuffer();
    while (...) {
       buffer.Append(someStuff());
    }
}

在你的情况下,常数更合适。

【讨论】:

  • StringBuffer 在编译时不构建字符串。
【解决方案2】:

将String定义为常量不是更好吗...

基本上是的。

... 因为它是线程安全的,而且我们知道它可以在编译时与加号运算符连接起来。

这些断言都是正确的。

但是,您无需担心带有StringBuffer 的代码版本中的任何线程安全问题。

  • StringBuffer 类是线程安全的。
  • 如果StringBuffer 实例只对一个线程可见(例如调用声明和使用该实例的方法的线程),那么该实例是线程受限,不需要是线程安全的数据结构。 (你可以改用StringBuilder ...)

使用+ 字面量连接的版本的主要优点是它在运行时花费零时间,并且不会导致对象分配......除了表示分配的连接字符串常量的一个 String 对象当你的类被加载时。


事实上,在很多地方,人们明确使用StringBuilderStringBuffer 来“优化”字符串连接,要么没有效果,要么实际上使代码变慢:

    1234563 >
  • 此外,Java 编译器通常会将表达式中的非常量字符串连接(使用 +)转换为使用 StringBuilder 的等效代码。

唯一值得明确使用StringBuilder 的情况是当sting 构建跨越多个语句时;例如因为你在一个循环中连接东西。

【讨论】:

  • 我不担心线程安全,因为我知道StringBuffer 是线程安全的,但这是否意味着将分配更多内存?每个线程一个新实例?
  • StringBuffer 版本每次运行时都会分配一个新的 StringBuffer 对象……除非您尝试重用现有对象。 (如果你这样做,你确实需要担心多线程......)
  • 那么,StringBuffer 的内容会在编译时拼接起来吗?
  • 不,不会的。 StringBuffer.append 调用不能由 javac 编译器组合,我怀疑 JIT 编译器也会这样做。
【解决方案3】:

我更喜欢第二种解决方案(仅使用+ 运算符)。

为什么?因为:

  • 更具可读性
  • 更多功能(面向函数式编程,当今时尚且高效)避免无用(临时)局部变量,尤其是可变变量(如buffer is)。

【讨论】:

    【解决方案4】:

    在以下代码中,缓冲区是否会在编译时附加其内容?

    是的。

    当多个线程试图执行这个函数时,它会如何表现?

    没问题,因为每个线程都会使用它自己的StringBuffer(它在方法中声明)。

    将String定义为常量不是更好吗?

    是的,在这里会更有意义。

    【讨论】:

    • 在本例中,StringBuffer 提供的线程安全性自您的第一条语句以来并未考虑在内:每个线程都将使用它自己的StringBuffer
    • StringBuffer 线程安全无关紧要 - 变量在方法体内声明,因此它存在并且只能在该线程中的该方法中访问(除非进一步传递等)。
    • 每个线程都会使用它自己的StringBuilder,这是否意味着我将为每个线程分配一个新的StringBuffer?与其他替代方案相比如何?
    • 我知道StringBuffer 的线程安全不会在这里发挥作用,但我想我也可以添加该信息。没关系,删除无关信息。
    • @Sednus:是的,因此在您的示例中使用常量更有意义。
    猜你喜欢
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 2015-09-15
    • 2011-09-23
    • 2010-12-30
    • 1970-01-01
    • 2019-05-22
    相关资源
    最近更新 更多