StringBuilder 在多线程环境下不安全的原因

  面试经常会问到,StringBuilder和StringBuffer的区别,大家都回答StringBuilder是线程不安全的,StringBuffer是线程安全的,那么StringBuilder到底是哪里不安全了呢?

public class DemoUtil {

    public static void main(String[] args) throws Exception{
       StringBuilder stringBuilder = new StringBuilder();
        for(int j=0;j<10;j++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<100;i++){
                        stringBuilder.append("2");
                    }
                }
            }).start();
        }

        Thread.sleep(100);
        System.out.println(stringBuilder.length());

    }

}

如上面所写的例子,创建了10个线程,每个线程循环100次,向stringBuilder中添加100个‘2’,如果结果正常的话,stringBuilder的长度应该是10000,但是你可以自己执行下,一般结果都是小于10000的,如果StringBuilder 换成StringBuffer的话,结果正好是10000。从这个例子的结果就可以看出StringBuilder是线程不安全的,那么到底哪里不安全了呢?

进入StringBuilder的append()方法,

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

也就是说StringBuilder的append方法是继承来的,那么它继承了谁呢?

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

StringBuilder继承了AbstractStringBuilder,我们再来看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;
    }

从count += len;这行代码我们就可以看到他肯定不是一个原子操作,这是由2步组合成的,我们都知道线程不安全一般都是因为多个线程共享变量导致的,那么这里的不安全到底是count变量造成的,还是len这个变量造成的呢?还是说这2个变量都会导致线程安全问题呢?

其实这里的线程不安全是由count这个变量造成的,len这个变量不会导致线程不安全,为什么呢?

因为我们可以看到len这个变量是在append方法内定义的,方法内部定义的变量是在运行时动态生成的。每个线程都有一个自己的堆栈,用于保存运行时的数据。每次调用时,变量都是在运行时堆栈上保存的,方法结束变量也就释放了。因此len变量是不会被多个线程共享的。

再来看count 变量的定义,count是AbstractStringBuilder的成员变量,能够被多个线程共享。因此

AbstractStringBuilder的线程不安全问题就出在count这个变量上。

那么StringBuffer又是怎么保证线程安全的呢?

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

直接在这个append的方法上加了synchronized锁,因此保证了线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值