Difference between StringBuilder and StringBuffer

后端 未结 30 2410
独厮守ぢ
独厮守ぢ 2020-11-21 15:06

What is the main difference between StringBuffer and StringBuilder? Is there any performance issues when deciding on any one of these?

30条回答
  •  广开言路
    2020-11-21 15:29

    In single threads, StringBuffer is not significantly slower than StringBuilder, thanks to JVM optimisations. And in multithreading, you can't use safely a StringBuilder.

    Here is my test (not a benchmark, just a test) :

    public static void main(String[] args) {
    
        String withString ="";
        long t0 = System.currentTimeMillis();
        for (int i = 0 ; i < 100000; i++){
            withString+="some string";
        }
        System.out.println("strings:" + (System.currentTimeMillis() - t0));
    
        t0 = System.currentTimeMillis();
        StringBuffer buf = new StringBuffer();
        for (int i = 0 ; i < 100000; i++){
            buf.append("some string");
        }
        System.out.println("Buffers : "+(System.currentTimeMillis() - t0));
    
        t0 = System.currentTimeMillis();
        StringBuilder building = new StringBuilder();
        for (int i = 0 ; i < 100000; i++){
            building.append("some string");
        }
        System.out.println("Builder : "+(System.currentTimeMillis() - t0));
    }
    

    Results :
    strings: 319740
    Buffers : 23
    Builder : 7 !

    So Builders are faster than Buffers, and WAY faster than strings concatenation. Now let's use an Executor for multiple threads :

    public class StringsPerf {
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
            //With Buffer
            StringBuffer buffer = new StringBuffer();
            for (int i = 0 ; i < 10; i++){
                executorService.execute(new AppendableRunnable(buffer));
            }
            shutdownAndAwaitTermination(executorService);
            System.out.println(" Thread Buffer : "+ AppendableRunnable.time);
    
            //With Builder
            AppendableRunnable.time = 0;
            executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
            StringBuilder builder = new StringBuilder();
            for (int i = 0 ; i < 10; i++){
                executorService.execute(new AppendableRunnable(builder));
            }
            shutdownAndAwaitTermination(executorService);
            System.out.println(" Thread Builder: "+ AppendableRunnable.time);
    
        }
    
       static void shutdownAndAwaitTermination(ExecutorService pool) {
            pool.shutdown(); // code reduced from Official Javadoc for Executors
            try {
                if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                    if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                        System.err.println("Pool did not terminate");
                }
            } catch (Exception e) {}
        }
    }
    
    class AppendableRunnable implements Runnable {
    
        static long time = 0;
        T appendable;
        public AppendableRunnable(T appendable){
            this.appendable = appendable;
        }
    
        @Override
        public void run(){
            long t0 = System.currentTimeMillis();
            for (int j = 0 ; j < 10000 ; j++){
                try {
                    appendable.append("some string");
                } catch (IOException e) {}
            }
            time+=(System.currentTimeMillis() - t0);
        }
    }
    

    Now StringBuffers take 157 ms for 100000 appends. It's not the same test, but compared to the previous 37 ms, you can safely assume that StringBuffers appends are slower with multithreading use. The reason is that the JIT/hotspot/compiler/something makes optimizations when it detects that there is no need for checking locks.

    But with StringBuilder, you have java.lang.ArrayIndexOutOfBoundsException, because a concurrent thread tries to add something where it should not.

    Conclusion is that you don't have to chase StringBuffers. And where you have threads, think about what they are doing, before trying to gain a few nanoseconds.

提交回复
热议问题