美文网首页
StringBuffer和StringBuilder的线程安全性

StringBuffer和StringBuilder的线程安全性

作者: 蘑菇姐夫 | 来源:发表于2020-04-14 12:57 被阅读0次

    一、概述

    在java开发时,我们经常会用到StringBuffer和StringBuilder,且都知道一个结论:StringBuilder不是线程安全的,StringBuffer是线程安全的,至于为什么?可能大多数人一知半解。
    下面,我通过代码举例、StringBuffer和StringBuilder源码分析进行解释。

    二、代码举例

    1、验证StringBuffer是线程安全的

        /**
         * 验证StringBuffer线程安全,如下,如果length==1000,则可证明
         * @throws InterruptedException
         */
        public static void testStringBuffer() throws InterruptedException {
            StringBuffer sb = new StringBuffer();
            for (int i=0; i<10; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j=0; j<1000; j++){
                            sb.append("a");
    
                        }
                    }
                }).start();
            }
    
            Thread.sleep(100);
            System.out.println(sb.length());
        }
    
    /**
         * 主测试方法
         * @param args
         */
      public static void main(String[] args) {
            try {
                ThreadTest.testStringBuffer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    

    上面,我们起10个线程,每个线程循环1000次往StringBuffer对象里面append字符。理论上应该输出实例sb的字符串长度=10000,我们执行代码,实际输出也是10000,业务上证明了StringBuffer是线程安全的。

    StringBuffer.png

    2、验证StringBuilder是线程不安全的

        /**
         * 验证StringBuild线程不安全,如下,如果length!=1000,则可证明
         * @throws InterruptedException
         */
        public static void  testStringBuild() 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<1000; j++){
                            sb.append("a");
                        }
                    }
                }).start();
            }
    
            Thread.sleep(100);
            System.out.println(sb.length());
    
        }
    
    /**
         * 主测试方法
         * @param args
         */
      public static void main(String[] args) {
            try {
                ThreadTest.testStringBuild();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    

    上面,我们也起10个线程,每个线程循环1000次往StringBuilder对象里面append字符。我们也希望像StringBuffer一样,得到10000的结果,但是我们运行代码后,结果却<10000。业务上证明了StringBuilder是线程不安全的。

    StringBuilder.png

    三、源码分析

    我们通过查看StringBuffer和StringBuilder的append()方法,发现他们都调用父类AbstractStringBuilder的append()方法,分别如下:

    StringBuffer重写的append方法:

      @Override
        public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
    

    StringBuilder重写的append方法:

        @Override
        public StringBuilder append(String str) {
            super.append(str);
            return this;
        }
    

    父类AbstractStringBuilder的append()方法:

       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;
        }
    

    通过上面的源码,可知,StringBuffer和StringBuilder的append()的区别就是StringBuffer多了个 toStringCache = null; 这里,我们不再分析AbstractStringBuilder的append()方法的实现方式,大家可以自行了解,下面主要分析影响线程安全性的这段代码的作用。

    我们查看StringBuffer源码,发现多了比StringBuilder多了一个参数:

     public final class StringBuffer
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence
    {
    
        /**
         * A cache of the last value returned by toString. Cleared
         * whenever the StringBuffer is modified.
         */
        private transient char[] toStringCache;
      //其他代码,略
    }
    

    再看下StringBuffer的toString方法:

        @Override
        public synchronized String toString() {
            if (toStringCache == null) {
                toStringCache = Arrays.copyOfRange(value, 0, count);
            }
            return new String(toStringCache, true);
        }
    

    这里的作用就是如果StringBuffer对象此时存在toStringCache,在多次调用其toString方法时,其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点,也是StringBuffer为什么线程安全的原因。

    四、结论

    通过上面的举例和源码分析,我们可以知道为什么StringBuffer是线程安全的,StringBuilder是线程不安全的,至于他们的append()方法,都是集成父类的append()方法,大家可以网上去了解下具体实现原理。

    相关文章

      网友评论

          本文标题:StringBuffer和StringBuilder的线程安全性

          本文链接:https://www.haomeiwen.com/subject/ztezmhtx.html