What does "StringBuilders are not thread safe" mean?

I have read several articles on the pros and cons String

and StringBuilder

in the Java programming language. In one of the articles, the author mentioned that:

StringBuilder is not thread safe , so use StringBuffer across multiple threads .

Unfortunately, I can't figure out what this 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 provide some sample code.

+7


source to share


4 answers


If multiple threads are modifying the same instance StringBuilder

, the result may be unexpected - i.e. some of the changes may be lost. This is why you should use StringBuffer in situations like this. If, however, each thread StringBuilder

can only be modified by one thread, it is better to use StringBuilder

it as it will be more efficient (thread safety is related to performance).



+13


source


If multiple threads try to change the value of the StringBuilder object, the result will be strange. See the example below,

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 threads call the addProperty method, the result will be strange (unpredictable).



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

      

Finally, when you call sb.toString (), the result is unpredictable. For example, it might output the result as 1=2,ac=d=b,e=f

, but your expectation would be1=2,a=b,c=d,e=f

+6


source


A thread safety issue with c StringBuilder

is that the method call is StringBuilder

not synchronized.

Consider the implementation of the method 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;
 }

      

Now, suppose you have two threads sharing an instance StringBuilder

and both are trying to add a character at the same time. Suppose they both enter the operator at the same time value[count++] = c;

, and count

- 1

. Each of them will write their character to the buffer in value[1]

, and then update count

. Obviously, only one character can be stored there ... so the other will be lost. Also, one of the increments count

is likely to be lost.

Worse, the operator value[count++] = c;

can crash even if both threads don't arrive there at the same time. The reason is that the Java memory model says that if proper synchronization does not exist (the "occurs before" relationship), the second thread is not guaranteed to see the memory updates created by the first thread. What actually happens depends on whether and when the first stream updates are written to main memory.


Now let's look at StringBuffer.append(char)

:

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

      

Here we can see that the method append

is equal synchronized

. This means two things:

  • Two threads cannot simultaneously execute a superclass method append

    on the same object StringBuffer

    . Thus, the first scenario cannot happen.

  • synchronize

    means exists happens before

    between successive calls made StringBuffer.append

    by different threads. This means that the last thread is guaranteed to see the updates made in the previous one.


The case String

is different again. If we look at the code, we can see that there is no explicit synchronization. But that's okay, because the object String

is actually immutable; those. there are no methods in the API String

that will cause an externally observable change in the state of the object String

. Besides:

  • The special behavior of instance variables final

    and constructors means that all threads will see the correct initial state for anyone String

    .

  • In one place where String

    changed behind the scenes, the method hashCode()

    will work correctly regardless of whether the thread sees the last changes in the variable hash

    .


Literature:

+4


source


Because StringBuilder is not synchronized whereas StringBuffer is synchronized. When using a StringBuilder in a multithreaded environment, multiple threads can simultaneously capture the StringBuilder object, and the output it produces cannot be predicted, so the StringBuilder is not thread safe ...

By using StringBuffer, we can overcome the thread safety issue where StringBuffer is thread safety because it synchronizes when only one thread can access at a time, so the output it produces can be expected and predictable.

+2


source







All Articles