String、StringBuffer、StringBuilder的应用场景

结论

String:不需要修改字符串,或者修改次数极少 。(String的+=其实不保证线程安全,String的+=操作底层是StringBuilder实现的,线程不安全)

StringBuffer:需要频繁修改字符串,多线程场景,需要保证线程的安全。

StringBuilder:需要频繁修改字符串,单线程场景,需要修改速度快。

原因

为什么String的访问速度快,为什么不能修改

String底层是一个常量,访问一个常量很快的,常量不支持修改。

java 复制代码
private final char value[]

既然String不能被修改,那+=是怎么回事

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s = "123";
        s += "456";
        System.out.println(s);
    }
}

输出

out 复制代码
123456

这样看起来,好像是在s后面添加了字符串"456",但是实际上,是改变了s的引用

下面这段代码可以验证,s已经不是之前的s了。

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s = "123";
        String b = s;
        System.out.println(b == s);
        s += "456";
        System.out.println(b);
        System.out.println(b == s);
    }
}

输出

out 复制代码
true
123
false

所以String的修改不是修改原字符串,而是替换字符创指向的引用。

真正的修改原来字符串会有下面的现象

java 复制代码
public class Main {
    public static void main(String[] args) {
        StringBuilder s = new StringBuilder("123");
        StringBuilder b = s;
        System.out.println(b == s);
        s.append("456");
        System.out.println(b);
        System.out.println(b == s);
    }
}

输出

out 复制代码
true
123456
true

验证StringBuilder线程不安全,StringBuffer线程安全

通过对比多线程场景下 StringBuilderStringBuffer 的拼接结果,验证二者的线程安全性:

一个线程负责在末尾添加"1",另一个线程负责在末尾添加"2"。

假如线程安全,那么要么全是1后面接全是2,要么全是2后面接全是1

如果结果不是这两个,那么线程不安全。

java 复制代码
public class Main {
    public static void main(String[] args) throws InterruptedException {
        int repeatCount = 100000;
        String result1 = "1".repeat(repeatCount) + "2".repeat(repeatCount);
        String result2 = "2".repeat(repeatCount) + "1".repeat(repeatCount);

        StringBuilder stringBuilder = new StringBuilder();
        Thread thread1 = new Thread(() -> {
            stringBuilder.append("1".repeat(repeatCount));
        });
        Thread thread2 = new Thread(() -> {
            stringBuilder.append("2".repeat(repeatCount));
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(stringBuilder.toString().equals(result1) || stringBuilder.toString().equals(result2));

        StringBuffer stringBuffer = new StringBuffer();
        thread1 = new Thread(() -> {
            stringBuffer.append("1".repeat(repeatCount));
        });
        thread2 = new Thread(() -> {
            stringBuffer.append("2".repeat(repeatCount));
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(stringBuffer.toString().equals(result1) || stringBuffer.toString().equals(result2));
    }
}

输出

out 复制代码
false
true

如果抛出ArrayIndexOutOfBoundsException,也能证明,ArrayIndexOutOfBoundsExceptionStringBuilder 线程不安全的典型严重表现 ,核心原因是多线程同时操作 StringBuilder 底层的字符数组时,导致数组扩容逻辑混乱,最终触发数组下标越界。

SrringBuffer线程安全的原因,StringBuilder速度快的原因

SrringBuffer对方法加了同步锁,保证了线程安全,但是限制了运行速度。

StringBuilder没有同步锁,所以线程不安全,由于没有锁的限制,执行速度却快。

面试

如果不修改或者修改次数极少,可以用String。

如果需要频繁修改,

多线程下,必须使用StringBuffer来保证线程安全。

单线程下,可以使用StringBuilder比StringBuffer快一些。

相关推荐
Languorous.1 分钟前
C++智能指针详解:原理、使用及避坑指南
开发语言·c++
老马95275 分钟前
opencode7-桌面应用实战2
java·人工智能·后端
m0_6091604911 分钟前
MySQL如何限制触发器递归调用的深度_防止触发器死循环方法
jvm·数据库·python
广州灵眸科技有限公司12 分钟前
瑞芯微(EASY EAI)RV1126B yolov11-track多目标跟踪部署教程
linux·开发语言·网络·人工智能·yolo·机器学习·目标跟踪
李白的天不白14 分钟前
大规模请求数据并发问题
java·前端·数据库
zjy2777718 分钟前
Golang bcrypt如何加密密码_Golang密码加密教程【收藏】
jvm·数据库·python
老纪32 分钟前
Redis怎样利用Lua为多个Key同步续期
jvm·数据库·python
2403_8832610934 分钟前
C#怎么计算两个日期的差值_C#如何处理时间跨度【笔记】
jvm·数据库·python
m0_7406532236 分钟前
Golang切片底层原理是怎样的_Golang切片实现原理教程【简明】
jvm·数据库·python
yexuhgu37 分钟前
CSS如何处理CSS逻辑属性兼容性_通过PostCSS转译为物理属性
jvm·数据库·python