彻底理解String、StringBuffer和StringBuilder

  • 区别: StringBuilder和StringBuffer的内部实现跟String类一样,都是通过一个char数组存储字符串的,不同的是String类里面的char数组是final修饰的,是不可变的,而StringBuilder和StringBuffer的char数组是可变的。

1. StringBuilder为什么线程不安全
查看JDK源码,知道append()方法

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

因为StringBuilder继承了AbstractStringBuilder,找到父类的append()方法


//存储字符串的数组
char[] value;
//已经使用的字符数组的数量
int count;


public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    //确保数组容量充足
    ensureCapacityInternal(count + len);
    //把str拷到value数组
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

我们可以发现count += len不是原子操作,在有多个线程操作的时候,会发生与期望值相悖的情况,造成总的count比期望的小。
第二个问题是会有可能抛出ArrayIndexOutOfBoundsException异常,比如有两个线程进行append操作,线程1进行ensureCapacityInternal扩容之后时间片用完,这时线程2执行append,刚好把容量用完,等线程1执行str.getChars时,就会抛出ArrayIndexOutOfBoundsException异常。

补充:
当容量不够时,扩容是原来2倍长再加2,如果还是不够则取minimumCapacity,最后新建数组把值拷贝过去


/**
     * This method has the same contract as ensureCapacity, but is
     * never synchronized.
     */
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
 
/**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        //扩容是原来2倍长再加2
        int newCapacity = value.length * 2 + 2;
        //新容量比str.length()+value.length短的话
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        //创建一个新的数组,把值拷贝过去
        value = Arrays.copyOf(value, newCapacity);
    }
 
public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

1. StringBuffer为什么线程安全
和StringBuilder一样,StringBuffer也是AbstractStringBuilder的子类。不同的是,StringBuffer在append和delete操作时加了同步锁。

public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
 
public synchronized StringBuffer delete(int start, int end) {
        toStringCache = null;
        super.delete(start, end);
        return this;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值