StringBuffer和StringBuilder的线程安全性

2020-04-14  本文已影响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()方法,大家可以网上去了解下具体实现原理。

上一篇下一篇

猜你喜欢

热点阅读