Search code examples
javastringbuilderstringbuffer

Testing the difference between StringBuilder and StringBuffer


I just want to see the difference between them visually, so below is the code. But it always fails. Can someone please help me on this? I have seen questions on SO too, but none of them have shown the difference programatically.

public class BBDifferencetest {
     protected static int testnum = 0;

     public static void testStringBuilder() {
             final StringBuilder sb = new StringBuilder();

             Thread t1 = new Thread() {

                     @Override
                     public void run() {
                             for (int x = 0; x < 100; x++) {
                                     testnum++;
                                     sb.append(testnum);
                                     sb.append(" ");                                
                             }
                     }
             };
             Thread t2 = new Thread() {

                     public void run() {

                             for (int x = 0; x < 100; x++) {
                                     testnum++;
                                     sb.append(testnum);
                                     sb.append(" ");
                             }
                     }
             };
             t1.start();
             t2.start();

             try {
                     t1.join();
                     t2.join();
             } catch (InterruptedException e) {
                     e.printStackTrace();
             }

             System.out.println("Result is: " + sb.toString());

        }

     public static void main(String args[]) {
             testStringBuilder();
     }
}

When I execute this, I get the output sometimes in a random manner, so this proves my test. But when I even replace StringBuilder with StringBuffer and test, even it gives me unexpected output(rather than sequential which from 1 to 200). So can someone help me getting to know the difference visually?

P.S : If anyone has your code which shows the difference, I would be very glad to accept it as an answer. Because I am not sure whether I can achieve the difference with my code even though it is modified.


Solution

  • (rather than sequential which from 1 to 200)

    Each thread is performing a read, modify, write operation on testnum. That in itself is not thread-safe.

    Then each thread is fetching the value of testnum again in order to append it. The other thread may well have interrupted by then and incremented the value again.

    If you change your code to:

    AtomicInteger counter = new AtomicInteger();
    ...
    sb.append(counter.getAndIncrement());
    

    then you're more likely to see what you expect.

    To make it clearer, change your loops to only call append once, like this:

    for (int x = 0; x < 100; x++) {
        sb.append(counter.incrementAndGet() + " ");
    }
    

    When I do that, for StringBuffer I always get "perfect" output. For StringBuilder I sometimes get output like this:

    97 98 100    102     104
    

    Here the two threads have both been appending at the same time, and the contents have been screwed up.

    EDIT: Here's a somewhat shorter complete example:

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            final AtomicInteger counter = new AtomicInteger();
            // Change to StringBuffer to see "working" output
            final StringBuilder sb = new StringBuilder();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    for (int x = 0; x < 100; x++) {
                        sb.append(counter.incrementAndGet() + " ");
                    }
                }
            };
    
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(sb);
        }
    }