14

I am building up a String out of multiple pieces and want to use either StringBuffer or StringBuilder to do so. From the Java 5 docs, I see that StringBuilder is preferred when possible, with the caveat that

Instances of StringBuilder are not safe for use by multiple threads.

From this statement, I understand that I should not have a single StringBuilder instance shared by multiple threads. But what about this case:

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

Here there could be multiple threads in the function at the same time, using the StringBuilder class at the same time (eg, concurrent access of static variables, if there are any), but each thread would have its own separate instance of StringBuilder. From the documentation, I can not quite decide whether this counts as use by multiple threads or not.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Ben
  • 221
  • 1
  • 3
  • 5

5 Answers5

19

That's perfectly fine. Local variables have no problems with thread safety as long as they don't access or mutate instance or class variables.

Michael Myers
  • 188,989
  • 46
  • 291
  • 292
11

Yes, that is safe, because the StringBuilder object is used only locally (each thread calling foo() will generate its own StringBuilder).

You should also note that the code you posted is practically identical to the bytecode generated by this:

String foo(String a, String b) {
    return a + b;
}
Kip
  • 107,154
  • 87
  • 232
  • 265
  • see also: http://stackoverflow.com/questions/272535/how-do-i-decompile-java-class-files – Kip Mar 10 '09 at 18:40
  • well ok not identical, it has one extra instruction.. the compiler converts + operator to: new StringBuilder().append(a).append(b); – Kip Mar 10 '09 at 18:48
  • Woah! I wouldn't have believed that unless I tried it. The only difference is there is additonal .append call in this answer, one for each argument. Hmm. – djangofan Oct 29 '16 at 17:30
6

The code you have is safe.

This code is not.

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);
    }
}

Local variables that only make use of local data do not have threadsafe issues. You can only have threadsafe issues once you start dealing with data that is visible at the class or instance method/variable level.

Mr.Q
  • 4,316
  • 3
  • 43
  • 40
TofuBeer
  • 60,850
  • 18
  • 118
  • 163
4

Agree with the other answers--just a note.

If there was a case where StringBuffer was being used by multiple threads, it's probably a completely broken use case because it would mean a single string was being built up in a quasi-random order, so it wouldn't make sense to make StringBuffer thread safe.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • That's the rationale of StringBuilder. Most of the times the synchronization was not needed. – OscarRyz Mar 10 '09 at 18:35
  • Yes, which makes you wonder why they didn't just rewrite StringBuffer instead of creating the parallel StringBuilder. To keep backwards compatibility for that app that builds nondeterministic strings? – Steve B. Mar 10 '09 at 18:35
  • 1
    Maybe if you were using a StringBuilder for some kind of in-memory logging of a multithreaded app? Not sure why you'd do that though... – Kip Mar 10 '09 at 18:39
  • 1
    @Kip That's the only thing I could come up with either, and when I tried to justify it, it seemed like a broken use case which fit my original statement so I left it out :) – Bill K Mar 10 '09 at 18:43
  • yeah, and if you really wanted to do that a Vector or synchronized ArrayList would probably be more appropriate than a StringBuilder – Kip Mar 10 '09 at 18:51
  • 1
    There is memory overhead for every object in those lists. A StringBuffer allows you to cut down on memory usage in lieu of more synchronized calls. – kjb Nov 14 '13 at 20:06
  • If you are writing whole lines in a thread and the order of lines is not important then it makes sense – MikeKulls Jun 02 '17 at 01:00
  • If that's the case I'd use a thread-safe list of strings and join them at the end instead--heck if the order isn't important maybe a map! – Bill K Jun 02 '17 at 02:17
3

I am not sure if this code is needed, because Java picks the StringBuilder automatically I guess. If you do not have a performance problem, go with a + b.

In case of a performance need, try that:

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

It correctly sizes the buffer and prevents the VM from resizing it and creating garbage to collect on the way.

ReneS
  • 3,535
  • 2
  • 26
  • 35