StringBuffer和StringBuilder的区别,从源码角度分析

引言

很多人都知道String是不可变的,StringBuffer和StringBuilder是可变的,那么为什么呢?
首先我们确定一个概念性问题,什么是不可变对象!
什么是不可变对象:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

关于Spring是不可变的可参考:Spring为什么是不可变的

StringBuffer和StringBuilder的区别

  1. 继承关系
    在这里插入图片描述在这里插入图片描述
    由图可知,StringBuffer和StringBuilder都继承了AbstractStringBuilder
  2. 线程安全
    这是它们的主要区别,主要在于一个关键字,synchronized

看过StringBuffer源码应该会知道, 几乎都是所有方法都加了synchronized。 用synchronized关键字修饰意味着什么?加锁,资源同步串行化处理,所以是线程安全的
而看过StringBuilder源码可知,基本上所有方法都没有用synchronized关键字修饰,当多线程访问时,就会出现线程安全性问题

为了证明StringBuffer线程安全,StringBuilder线程不安全,我们通过一段代码进行验证:

测试思想
分别用1000个线程写StringBuffer和StringBuilder,
使用CountDownLatch保证在各自1000个线程执行完之后才打印StringBuffer和StringBuilder长度,观察结果。

测试代码

@Test
public void testStringBufferAndStringBuiler(){
    //证明StringBuffer线程安全,StringBuilder线程不安全
    StringBuffer stringBuffer = new StringBuffer();
    StringBuilder stringBuilder = new StringBuilder();
    CountDownLatch latch1 = new CountDownLatch(1000);
    CountDownLatch latch2 = new CountDownLatch(1000);
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    stringBuilder.append(1);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch1.countDown();
                }
            }
        }).start();
    }
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    stringBuffer.append(1);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch2.countDown();
                }
            }
        }).start();
    }
    try {
        latch1.await();
        System.out.println(stringBuilder.length());
        latch2.await();
        System.out.println(stringBuffer.length());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

测试结果

* StringBuffer不论运行多少次都是1000长度。
* StringBuilder绝大多数情况长度都会小于1000。
* StringBuffer线程安全,StringBuilder线程不安全得到证明。

总结

  • StringBuffer和StringBuilder都继承自抽象类AbstractStringBuilder。
  • 对于拼接字符串效率要比String要高:存储数据的字符数组没有被final修饰,说明值可以改变,内部可自动扩容
  • 线程安全性:StringBuffer效率低,线程安全,因为StringBuffer中很多方法都被 synchronized 修饰了,多线程访问时,线程安全,但是效率低下,因为它有加锁和释放锁的过程。StringBuilder效率高,但是线程是不安全的
发布了30 篇原创文章 · 获赞 12 · 访问量 3431

猜你喜欢

转载自blog.csdn.net/zx1293406/article/details/103919258