皮皮网
皮皮网

【网站源码插件】【dbeaver 源码分析】【绿线源码】stringbuffer源码解析

来源:多商户抢单源码 发表时间:2024-11-30 06:00:51

1.StringBuilder为什么线程不安全?
2.String,StringBuffer和StringBuilder的区别
3.stringbuffer与stringbuilder的区别?源码
4.stringbuffer扩容为什么是2倍+2?

stringbuffer源码解析

StringBuilder为什么线程不安全?

       1、为什么输出值跟预期值不一样

       æˆ‘们先看一下StringBuilder的两个成员变量(这两个成员变量实际上是定义在AbstractStringBuilder里面的,StringBuilder和StringBuffer都继承了AbstractStringBuilder)

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

       å†çœ‹StringBuilder的append()方法:

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

       StringBuilder的append()方法调用的父类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不是一个原子操作。假设这个时候count值为,len值为1,两个线程同时执行到了第七行,拿到的count值都是,执行完加法运算后将结果赋值给count,所以两个线程执行完后count值为,而不是。这就是为什么测试代码输出的值要比小的原因。

       2、为什么会抛出ArrayIndexOutOfBoundsException异常。

       æˆ‘们看回AbstractStringBuilder的append()方法源码的第五行,ensureCapacityInternal()方法是检查StringBuilder对象的原char数组的容量能不能盛下新的字符串,如果盛不下就调用expandCapacity()方法对char数组进行扩容。

       private void ensureCapacityInternal(int minimumCapacity) {         // overflow-conscious code    if (minimumCapacity - value.length > 0)        expandCapacity(minimumCapacity);}

       æ‰©å®¹çš„逻辑就是new一个新的char数组,新的char数组的容量是原来char数组的两倍再加2,再通过System.arryCopy()函数将原数组的内容复制到新数组,最后将指针指向新的char数组。

       void expandCapacity(int minimumCapacity) {     //计算新的容量    int newCapacity = value.length * 2 + 2;    //中间省略了一些检查逻辑    ...    value = Arrays.copyOf(value, newCapacity);}

       Arrys.copyOf()方法

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

       AbstractStringBuilder的append()方法源码的第六行,是将String对象里面char数组里面的内容拷贝到StringBuilder对象的char数组里面,代码如下:

       str.getChars(0, len, value, count);

       getChars()方法

       public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {     //中间省略了一些检查    ...       System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);    }

       æ‹·è´æµç¨‹è§ä¸‹å›¾

       å‡è®¾çŽ°åœ¨æœ‰ä¸¤ä¸ªçº¿ç¨‹åŒæ—¶æ‰§è¡Œäº†StringBuilder的append()方法,两个线程都执行完了第五行的ensureCapacityInternal()方法,此刻count=5。

       è¿™ä¸ªæ—¶å€™çº¿ç¨‹1的cpu时间片用完了,线程2继续执行。线程2执行完整个append()方法后count变成6了

       çº¿ç¨‹1继续执行第六行的str.getChars()方法的时候拿到的count值就是6了,执行char数组拷贝的时候就会抛出ArrayIndexOutOfBoundsException异常。

       è‡³æ­¤ï¼ŒStringBuilder为什么不安全已经分析完了。如果我们将测试代码的StringBuilder对象换成StringBuffer对象会输出什么呢?

       å½“然是输出啦!

       é‚£ä¹ˆStringBuffer用什么手段保证线程安全的?这个问题你点进StringBuffer的append()方法里面就知道了。

String,StringBuffer和StringBuilder的区别

       java中String、StringBuffer、StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题。现在总结一下,看看他们的不同与相同。

       1.可变与不可变

       ã€€ã€€String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。

       ã€€ã€€ã€€ã€€private final char value[];

       ã€€ã€€StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。

       ã€€ã€€ã€€ã€€char[] value;

       2.是否多线程安全

       ã€€ã€€String中的对象是不可变的,也就可以理解为常量,显然线程安全。

       ã€€ã€€AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

       ã€€ã€€StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。看如下源码:

1 public synchronized StringBuffer reverse() {

       2     super.reverse();

       3     return this;

       4 }

       5 

       6 public int indexOf(String str) {

       7     return indexOf(str, 0);        //存在 public synchronized int indexOf(String str, int fromIndex) æ–¹æ³•

       8 }

       ã€€ã€€StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

        3.StringBuilder与StringBuffer共同点

       ã€€ã€€StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。

       ã€€ã€€æŠ½è±¡ç±»ä¸ŽæŽ¥å£çš„其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。

       ã€€ã€€StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。

       ã€€ã€€æœ€åŽï¼Œå¦‚果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。

stringbuffer与stringbuilder的区别?

       äºŒè€…的区别主要是在运行速度和线程安全这两方面。

       1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的

       2、只是StringBuffer 中的方法大都采用了 synchronized å…³é”®å­—进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

       3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低。

stringbuffer扩容为什么是2倍+2?

       在常规用法中,StringBuffer和StringBuilder在功能上差别不大,解析主要区别在于StringBuffer具备线程安全性,源码但效率相对较低,解析而StringBuilder则线程不安全但效率更高。源码不过在扩容机制上,解析网站源码插件两者一致。源码下面以StringBuffer为例进行深入分析。解析

       首先,源码追踪StringBuffer源码,解析发现它继承自AbstractStringBuilder。源码这意味着StringBuffer和StringBuilder是解析“亲兄弟”,拥有共同的源码dbeaver 源码分析抽象父类AbstractStringBuilder。在这个抽象类中,解析定义了字符串存储的源码定长字节数组,并在追加字符串时,当长度超过数组长度时,通过数组复制方式实现扩容。

       容量设置上,绿线源码StringBuffer提供了默认容量和自定义容量的构造方法,即使默认构造方法也会设置初始容量为。在实际开发中,容量不足时,通过append()方法追加字符串触发动态扩容。

       append()方法实际上调用的cstl源码解析是AbstractStringBuilder的append()方法,进入内部后,发现关键在于ensureCapacityInternal()方法。该方法确保内部容量足够,通过count+len参数计算追加后字符串总长度,实现动态扩容。

       在ensureCapacityInternal()方法中,odbus的源码首先利用二进制位右移运算计算数组原有容量,考虑到编码方式(默认Latin1或UTF-),判断新字符串长度是否超过原有容量。若超过,则利用Arrays.copyOf()方法复制并创建新数组,将当前数组值赋给新数组,完成扩容。

       newCapacity()方法计算扩容后数组长度,通常在新字符串长度基础上增加一定比例,确保足够容纳新追加的字符串。而新长度计算逻辑通常涉及Math.max()方法,确保不会超出Integer最大值,避免内存溢出异常。

       StringBuffer扩容机制核心如下:若一次追加字符串长度超过当前容量,扩容规则为当前容量*2+2;如果追加长度超出初始容量且按当前容量*2+2扩容后仍不足,直接扩容至与新字符串长度相等;后续追加继续遵循当前容量*2+2规则。扩容为2倍+2的原因是为了减少内存分配次数和内存碎片,提高性能和效率。

       为了验证上述规则,可设计实验案例,观察StringBuffer与StringBuilder的扩容表现。至此,详细解释了StringBuffer扩容机制及其规则,希望能对理解Java中字符串操作有所帮助。

相关栏目:热点