This is the fourth day of my participation in the August Text Challenge.More challenges in August

| author: jiangxia

| CSDN:blog.csdn.net/qq_41153943

| the nuggets: juejin. Cn/user / 651387…

| zhihu: www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

This article is about 2865 words long and 15 minutes is recommended

preface

Remember when I was asked in an interview about the difference between String, Stringbuffer, and StringBuilder. I said String is an immutable String, Stringbuffer, StringBuilder is a mutable String, Stringbuffer is thread-safe, StringBuilder is not thread-safe, so it can’t be accessed synchronously. I think this is stable, and then there is no then. Now that’s the kind of answer you’d expect to get. So today from the perspective of the source code to analyze the difference between these three strings in the end where.

1, the String

Let’s look at the definition of the String class:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; /** Use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count <= 0) { if (count < 0) { throw new StringIndexOutOfBoundsException(count); } if (offset <= value.length) { this.value = "".value; return; } } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }Copy the code

As we can see from the source of the String class, the class is modified by the final keyword, and the String class is actually an array of char[] modified by the final keyword, so the implementation details are not allowed to change. This is the Immutable property of the String class (do you know why the String class cannot be inherited?). Each operation on String generates a new String, which is inefficient and wastes a lot of limited memory space.

Here is a process for executing the code below:

String string ="abc";
string+="def";
Copy the code

String String =” ABC “;

String +=”def” (string+=”def”); string+=”def” (string+=”def”); abcDEF (string+=”def”); You can see that executing such a short two lines of code requires the heap to open up memory space three times, resulting in a significant waste of memory space resources.

But strings need to be manipulated frequently during programming, so Java introduces two classes that can handle such variations: The StringBuffer class and the StringBuild class.

StringBuffer and StringBuild

Or first on the source code:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{}
Copy the code
public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{}
Copy the code

Char array that is final

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
 
    private final char value[];
Copy the code

Stringbuilder and StringBuffer both inherit from AbstractStringBuilder but AbstractStringBuilder char arrays don’t use final, that’s why strings are immutable, But StringBuffers and StringBuilders are mutable

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
   
    char[] value;
 
    /**
     * The count is the number of characters used.
     */
    int count;
 
    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
Copy the code

StringBuffer and StringBuilder class structure

AbstractStringbuilder class AbstractBuffer class AbstractBuffer class AbstractBuffer class AbstractBuffer Then the Serializable, CharSequence interface is implemented. Second, the internal implementation of Stringbuilder and Stringbuffer is the same as String. It stores strings in an array of types called char, but the char array in String is final and immutable. Char arrays in StringBuilder and StringBuffer are not modified by final and are mutable. That’s the difference between StringBuilder and StringBuffer and String.

So why is StringBuilder and StringBuffer thread-safe and not? What if we use StringBuilder and StringBuffer separately in multiple threads?

Let’s look at the use of Stringbuffer. Stringbuffer is used in multiple threads:

public class Demo1 { public static void main(String[] args) throws InterruptedException { StringBuffer sb = new StringBuffer(); for(int i=0; i<10; @override public void run() {for(int j=0; j<10000; J++) {sb. Append (" um "); }} // thread start}).start(); Thread.sleep(300); Println (sb.length()); }}Copy the code

The final output is:

The result is the same as the theoretical result. Let’s take a look at the results of using StringBuilder, using StringBuilder in multithreading:

public class Demo2 { public static void main(String[] args) throws InterruptedException { StringBuilder sb = new StringBuilder(); for(int i=0; i<10; i++){ new Thread(new Runnable(){ @Override public void run() { for(int j=0; j<10000; J++) {sb. Append (" um "); } } }).start(); } Thread.sleep(300); System.out.println(sb.length()); }}Copy the code

The output is:

In theory, the result should be 100,000 like StringBuffer, but the actual result is 85560, which is different from the expected result and executes multiple times with different results (all less than expected), whereas StringBuffer executes multiple times with the same result. But sometimes ArrayIndexOutOfBoundsException anomalies (array index cross-border exception).

So we can see that using StringBuilder in multithreading is indeed thread-unsafe. Why is the actual output incorrect?

Abstractstringbuffer class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class AbstractStringBuilder class

abstract class AbstractStringBuilder implements Appendable, CharSequence {/** * The value is used for character storage. * 翻译 : char[] value; /** ** The count is The number of characters used. */ int count;Copy the code

A StringBuilder and a StringBuffer use an append method to increment strings. AbstractStringBuilder append is a StringBuilder append method.

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
Copy the code

AbstractStringBuilder > abstractStringBuilder > abstractStringBuilder > abstractStringBuilder < abstractStringBuilder >

public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); Count += len; return this; }Copy the code

An important concept in multithreaded programming is called atomic operations, and atomic operations are operations that are not interrupted by thread scheduling; This operation, once started, runs until the end, without switching to any other thread. Count +=len is not an atomic operation, it is the same as count=count+len. For example, in the appeal code, when the value of count is 99998, a new len of length 1 is created, but when two threads execute the value of count+=len at the same time, They count value is 99998, and then each execution count + = len, respectively is executed after the value is 99999, and then to assign values to the count, then the final result of the count is 99999, 100000, not the right So when executing StringBuilder in multiple threads, the value will always be smaller than the correct result.

But StringBuilder and StringBuffer both inherit abstractStringBuilder. Abstractstringbuilder’s append method is always the same. Let’s look at the append method in a stringBuffer, which is modified by synchronized:

@Override public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; Public synchronized StringBuffer appEnd (String STR) {toStringCache = null; toStringCache = null; super.append(str); return this;Copy the code

You can see that the append operation in stringBuffer is modified by the synchronized keyword. This keyword is certainly familiar, mainly used to ensure the synchronization of threads in multiple threads and ensure the accuracy of data. So using StringBuffers in multithreading is thread-safe.

AbstractStringBuilder append method has two operations like this:

ensureCapacityInternal(count + len); //1 str.getChars(0, len, value, count); / / 2Copy the code

This is a method that checks whether the array of the StringBuilder object is large enough to hold a new string. If not, create a new array. The new array is twice the size of the original char array. The contents of the original array are copied to the new array using the CopyOf() method.

/** * For positive values of {@code minimumCapacity}, this method * behaves like {@code ensureCapacity}, however it is never * synchronized. * If {@code minimumCapacity} is non positive due to numeric * overflow, this method throws {@code OutOfMemoryError}. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); }}Copy the code

The second step is to copy the contents of the char array of String into the CHAR array of StringBuilder. Getchars source code:

 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
//1       
 if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
//2   
     if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
//3   
     if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
Copy the code

Here you can see the original sell StringIndexOutOfBoundsException abnormalities.

If thread A and thread B both execute append and ensureCapacityInternal() at the same time, count is 99997. If thread A finishes, thread 2 will continue executing. After thread B completes the append method, count becomes 99998. If thread A executes the getchars method above, thread A will get 99998 count, which should have been 99997. So at this time will be thrown ArrayIndexOutOfBoundsException exception.

3. Summary:

String is an array of characters modified by the final modifier, so it is immutable. If you are operating on a small amount of data, you can use String.

StringBuilder and StringBuffer are mutable arrays of strings;

AbstractStringBuilder is thread unsafe because StringBuilder inherits its abstractStringBuilder append method, which has a count+=len operation that is not atomic, So in a multithreaded use StringBuilder will lose data accuracy and flip ArrayIndexOutOfBoundsException exception.

A StringBuffer is thread-safe because its append method is modified by the synchronized keyword, so it ensures thread synchronization and data accuracy.

Because StringBuffer is modified by synchronized, StringBuilder is more efficient than StringBuffer in single-threaded situations. So a StringBuilder is used for executing large amounts of data in a single thread, and a StringBuffer is used in the case of multiple threads.

Highlights from the past

  • Spring annotation (2) : @ComponentScan Automatically scans components
  • Spring is worth your collection!!
  • Spring annotation (7) : Assign attributes to beans using @value
  • SpringBoot develops Restful interfaces to implement CRUD functions
  • Introduction to distributed cache middleware Redis