Search code examples
javajava-8stringbuilder

Why String Builder is returning its length zero, while returning some value of it


In below program, String buffer is working as expected, but in case of string builder, the length of String builder is zero, while it's giving me some data. So my question is, if string builder has some value, how can it return its length zero?

public class SbVsSbf2 {

    static StringBuilder sb = new StringBuilder();
    static StringBuffer sbf = new StringBuffer();

    public static void main(String[] args) throws InterruptedException {
        // Thread 1
        Thread t1 = new Thread(() -> {
            for (int i = 0; i <= 23; i++) {
                sb.append(i);
                sbf.append(i);
            }
        });

        // Thread 2
        Thread t2 = new Thread(() -> {
            for (int i = 0; i <= 23; i++) {
                sb.append(i);
                sbf.append(i);
            }
        });

        t1.start();
        t2.start();

        System.out.println("StringBuilder: " + sb.length());
        System.out.println("StringBuffer: " + sbf.length());
        System.out.println("StringBuilder: " + sb);
        System.out.println("StringBuffer : " + sbf);

    }
}

Solution

  • This is mainly a matter of timing. You can replace StringBuilder with StringBuffer and get a similar result.

    For example, when I run the following program on my machine

    public class ThreadStart {
      static StringBuilder sb = new StringBuilder();
    
      static long firstAction;
    
      public static void main(String[] args) throws InterruptedException {
        
          Thread t1 = new Thread(() -> firstAction = System.nanoTime());
          long starting = System.nanoTime();
          t1.start();
          int length = sb.length();
          long afterReadingLength = System.nanoTime();
          String s = "StringBuilder: " + length;
          long afterConcatenation = System.nanoTime();
          System.out.println(s);
          long andPrintln = System.nanoTime();
          t1.join();
    
          System.out.printf("time until %-20s %,d ns%n",
                            "thread's 1st action", firstAction - starting);
          System.out.printf("time until %-20s %,d ns%n",
                            "reading sb.length()", afterReadingLength - starting);
          System.out.printf("time until %-20s %,d ns%n",
                            "string concatenation", afterConcatenation - starting);
          System.out.printf("time until %-20s %,d ns%n",
                            "println completion", andPrintln - starting);
      }
    }
    

    I get results similar to

    StringBuilder: 0
    time until thread's 1st action  399.000 ns
    time until reading sb.length()  181.300 ns
    time until string concatenation 195.300 ns
    time until println completion   392.800 ns
    

    though numbers can vary significantly between runs. It demonstrates the order of magnitude of the operations involved.

    Starting a thread takes a significant amount of time. That’s one of the reasons why we use thread pools; handing a task over to an already running worker thread is significantly faster than starting a new thread.

    Some of the work has been done within the start() method execution in the main thread. I did not try to separate it from the execution of sb.length() here, as the time measurement wouldn’t be precise enough. It’s clearly visible that the background thread is far away from doing anything by the time, sb.length() returned. Even the string concatenation is not expensive enough.

    But after the execution of the first println call, we get into the same order of magnitude, so overlapping of the background thread’s activity with the main thread may happen after that.


    It’s still important to emphasize that you must not rely on any assumed timing and changing an object from multiple threads can give you unpredictable results.