16

I have read some articles about the pros and cons of String and StringBuilder in the Java Programming language. In one of the articles, the author mentioned that:

StringBuilder is not Thread-safe, so in multiple threads use StringBuffer.

Unfortunately, I cannot understand what it means. Could you please explain the difference between String, StringBuilder and StringBuffer especially in the context of "Thread safety".

I would appreciate it if you could describe with the code example.

Elyas Hadizadeh
  • 3,289
  • 8
  • 40
  • 54
  • Refer this http://javahungry.blogspot.com/2013/06/difference-between-string-stringbuilder.html – Anptk Nov 11 '14 at 08:07
  • might be useful: [How do I prove programmatically that StringBuilder is not threadsafe?](https://stackoverflow.com/questions/48558432) – Andrew Tobilko Feb 19 '18 at 10:15

5 Answers5

18

If multiple threads are modifying the same instance of a StringBuilder, the result can be unexpected - i.e. some of the modifications may be lost. That's why you should use StringBuffer in such situations. If, however, each thread StringBuilder instance can be modified by only one thread, it is better to use StringBuilder, since it would be more efficient (thread safety comes with a performance cost).

Eran
  • 387,369
  • 54
  • 702
  • 768
  • And the major use-case is to build a string sequentially, well-formed, a StringBuilder/StringBuffer is used as local variable, without multiple thread sharing. So StringBuilder is the primary choice in most cases. – Joop Eggen Nov 11 '14 at 08:18
9

If multiple thread tries to change the StringBuilder object value then the result will be strange. See the below example,

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

If many thread calls addProperty method then the result will be strange (unpredictable result).

Thread1: addProperty("a", "b");
Thread2: addProperty("c", "d");
Thread3: addProperty("e", "f");

Finally when you call sb.toString() the result will be unpredictable. For example, it may bring output like 1=2,ac=d=b,e=f, but your expectation would be 1=2,a=b,c=d,e=f

Jaya Ananthram
  • 3,433
  • 1
  • 22
  • 37
  • 2
    Even if you used `StringBuffer` you could still get the unpredictable result you mention. It can happen that Thread2 makes 2 appends, then Thread3 makes 1 append, then Thread2 makes 2 appends etc. Depends on the thread management strategy used by the JVM. In order to avoid this, you would have to make the entire method `addProperty` synchronized. The difference with `StringBuffer` is it doesn't allow multiple threads to be in the `append` method itself (`append` is synchronized) therefore overwriting each others' changes - see Stephen's answer below. – Adam Burley Jan 31 '20 at 13:00
  • @AdamBurley Thanks for your response. But I don't understand why you explained StringBuffer to my answer. Question is about why StringBuilder is not thread safe, so I explained why it is not thread safe, but I haven't explained which is safe or how to make safe, because it was not the question actually. I haven't explained anything about StringBuffer in my answer. So for me it looks like your comment is irrelevant to my answer. – Jaya Ananthram Feb 01 '20 at 18:23
  • 1
    The question was about what it means for `StringBuilder` to not be thread-safe. But the issue mentioned in your answer has nothing to do with `StringBuilder` not being thread-safe. Even if you used a thread-safe version of `StringBuilder` (i.e. `StringBuffer`, which is why I mentioned `StringBuffer`) you would still get the issue mentioned in your answer. The problem which causes the issue you mention is that the code you wrote which calls `StringBuilder` is not thread-safe, and **not** that `StringBuilder` itself is not thread-safe. – Adam Burley Feb 02 '20 at 23:44
8

The thread-safety issue with StringBuilder is that method calls on a StringBuilder do not synchronize.

Consider the implementation of the StringBuilder.append(char) method:

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

Now suppose that you have two thread that are sharing a StringBuilder instance, and both attempt to append a character at the same time. Suppose that they both get to the value[count++] = c; statement at the same time, and that count is 1. Each one will write its character into the buffer at value[1], and then update count. Obviously only one character can be stored there ... so the other one will be lost. In addition, one of the increments to count will probably be lost.

Worse than that, the value[count++] = c; statement can fail even if the two threads don't arrive there at the same time. The reason is that the Java memory model says that unless there is proper synchronization (or more precisely, a happens before relationship), it is not guaranteed that the second thread will see the memory updates made by the first thread. What actually happens depends on whether and when the first thread's updates are written through to main memory.


Now lets look at StringBuffer.append(char):

public synchronized StringBuffer append(char c) {
    super.append(c);  // calls the "AbstractStringBuilder.append" method above.
    return this;
}

Here we see that the append method is synchronized. This means two things:

  • Two threads cannot execute the superclass append method on the same StringBuffer object at the same time. Thus the first scenario cannot happen.

  • The synchronize means that there is a happens before between successive calls to StringBuffer.append made by different threads. That means that the later thread is guaranteed to see the updates made in the earlier one.


The String case is different again. If we examine the code, we will see that there is no overt synchronization. But that's OK, because a String object is effectively immutable; i.e are no methods in the String API that will result in an externally observable change in the String object's state. In addition:

  • The special behaviour of final instance variables and constructors means that the all threads will see the correct initial state for any String.

  • In the one place where the String is mutable behind the scenes, the hashCode() method will work correctly whether or not a thread sees the most recent changes to the hash variable.


References:

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

Because StringBuilder is not a synchronized one whereas StringBuffer is a synchronized one.When using StringBuilder in a multithreaded environment multiple threads can acess the StringBuilder object simultaneously and the output it produces can't be predicted hence StringBuilder is not a thread safe...

Using StringBuffer we can overcome the problem of Threads safety where the StringBuffer is a thread safety because it is synchronized where only one thread can access at a time so the output it produces can be expected and predicted.

Harish N
  • 19
  • 1
0

StringBuilder inside a method would be safe.

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);
    }
}
Anish Abraham
  • 421
  • 4
  • 4