• 为什么说StringBuilder不安全?


    先看一下StringBuilder和StringBuffer的构成

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

    两个类继承了同一个类,也实现了同样的接口,那么导致线程不安全的方法肯定在类内部自身的方法了。

    再来看一下他们共同的父类

    abstract class AbstractStringBuilder implements Appendable, CharSequence {
    
        char[] value;
        int count;
        
    }

    该类有两个重要的成员变量 一个是count,一个是value,count是字符个数,也就是长度。value则为具体的字符数组。

    再来看一下常见的append(String str)方法

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

    讲解一下这个代码,首先数组的长度在创建的时候就是固定的,想一个原先大小为5的数组 想再添加n个元素 做法是创建一个新得5+n长度的数组,先优先放原先的数组,再在数组的长度(count)处开始添加需要添加进来的元素。

    String的本质就是char[] ,所以这个方法里的str.getChars()方法不难猜出是将原先字符数组和新传进来的字符数组大小相加,创建一个不小于该长度的新数组,先优先放入旧数组,再根据旧数组的长度作为索引存入新数组,在这个过程里,count有着举足轻重的作用,他标记了需要在新字符数组的哪个索引处开始添加。如果count混乱,最后的值肯定也是不准确的。所以关键点就是方法是否加锁。

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

    最后来看一下String.getChars()方法来验证一下以上是否正确

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
            if (srcBegin < 0) {
                throw new StringIndexOutOfBoundsException(srcBegin);
            }
            if (srcEnd > value.length) {
                throw new StringIndexOutOfBoundsException(srcEnd);
            }
            if (srcBegin > srcEnd) {
                throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
            }
           /*
    * @param value 新加入的数组
    * @param srcBegin 新加入的数组起始位置
    * @param det 原先数组
    * @param destPs 原先数组添加新元素的索引 也就是count
    * @param length 新加入数组的加入个数
    * @return 新得数组 长度为dst.count() +value.count() ,值为两部分,自身的[dst]和新加入的[value]
    * int[] arr={2,3};
    * int[] arr2={1,4,5};
    * System.arraycopy(arr,0,arr2,1,2);
    * 此时 arr2的值为[1,2,3]
    * 此处没有违背数组大小的长度无法改变的准则,只是arr2指向了另一个新得数组而已。
    */
    
    
            System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 
    }
    不和别人一样,不复制只真正理解
  • 相关阅读:
    数据库连接池的作用及c3p0的详解(转载他人的--合理掌握学习方式)
    JAVA读取propertise文件内容两种方式(起始还是有很多种的)
    servlet--生命周期
    UML学习(三)-----序列图
    UML学习(一)-----用例图
    UML学习(二)-----类图
    Java静态域与静态方法
    spring boot启动原理三(结合web容器,如:tomcat(默认),jetty)
    spring源码相关第五篇----------------------------spring tx实现原理源码解读
    spring源码相关第四篇----------------------------spring aop实现原理源码解读
  • 原文地址:https://www.cnblogs.com/Vinlen/p/13597079.html
Copyright © 2020-2023  润新知