再谈StringBuilder为什么线程不安全以及带来的问题

这篇具有很好参考价值的文章主要介绍了再谈StringBuilder为什么线程不安全以及带来的问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 缘起

比较有意思的是,学习锁消除的过程中,有人讲到StringBuffer在方法内构建,不会被其他方法引用时,StringBuffer的锁会被消除,
于是,顺便看了一下同源的StringBuidler为什么线程不安全,以及为什么多线程不安全,和带来的问题,
有了这篇文章,分享出来,帮助读者轻松应对知识交流与考核。

2 StringBuilder

StringBuilder用于缓存字符串的容器,是StringBuffer的高性能版本,因为,StringBuilder适用于单线程,多线程下无法保证程序正常执行。建议优先使用StringBuilder,多数场景下,效率更高。
StringBuilder继承AbstractStringBuilder,而StringBuffer也是继承该类,所以,StringBuilder和StringBuffer是兼容的。

位置:java.lang.StringBuilder
再谈StringBuilder为什么线程不安全以及带来的问题

2.1 StringBuilder线程不安全是指什么?

StringBuilder的多线程不安全是指程序不能正常执行,即数组越界异常,而不是数据错乱问题。

2.1.1 测试样例

package com.monkey.java_study.lock;

/**
 * 锁消除测试.
 *
 * @author xindaqi
 * @since 2023-06-23 16:04
 */
public class LockEliminateTest {

    static StringBuilder sb2 = new StringBuilder();

    public static void main(String[] args) throws Exception {
        LockEliminateTest lockEliminateTest = new LockEliminateTest();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    sb2.append("test");
                }
            }).start();
        }
        Thread.sleep(100);
        System.out.format(">>>>>>StringBuilder:length:%d\n", sb2.length());
    }
}

2.1.2 测试结果

多线程情况下,StringBuilder出现数组越界,无法正常添加数据,程序异常。
异常信息如下图所示:
再谈StringBuilder为什么线程不安全以及带来的问题

2.2 为什么线程不安全?

结论:因为AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
先从append方法说起,StringBuilder继承AbstractStringBuilder,而StringBuilder中直接使用了父类的append方法。
再谈StringBuilder为什么线程不安全以及带来的问题
既然StringBuilder直接使用了AbstractStringBuilder的append方法,我们直接探究这个父类的方法,源码如下,使用的全局变量count作为数组扩容的参数:ensureCapacityInternal。

再谈StringBuilder为什么线程不安全以及带来的问题
数组扩容系列操作源码如下,如果传入的值大于存储数据的字符数组长度,则拷贝数据到新的数组中,保证数据可以正常存储。
问题就出现在这里,多线程出现数组没有及时扩充,使用str.getChars时,导致数组越界。
现在推演一下:
缓存数据的数组arr长度为10,已占用6个空位,还剩4个空位
即初始count为6,
两个线程T1和T2,均携带4个字符,向数组写入数据,
此时:minimumCapacity=count+len=6+4=10
线程T1:
miniumCapacity-value.length=0不满足扩容条件,
此时数据写入arr,刚好填满,还未执行到count+=len时,
线程T2:
开始执行ensureCapacityInternal,此时,count+len=6+4=10,依旧不会触发扩容,
线程T1:
执行count+=len,即count=10,
T2执行到str.getChars时,count为10,出现数组不够用,导致数组越界。
即:T2执行str.getChars(0, 4, [a1, …, a10], 10)
srcBegin=0
srcEnd=4
dst=[a1, …, a10]
dstBegin=10
在826行出现异常,即Sysgem.arrayCopy
这个时候dst长度为10,而新写入的数据从10开始,因此,无法再写入数据了,抛出异常。

再谈StringBuilder为什么线程不安全以及带来的问题

再谈StringBuilder为什么线程不安全以及带来的问题
多线程出现的时间差,导致数组没有正常被扩容,
最后无法写入数据,数组越界后抛出异常。
测试样例:

char[] dst = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
        String str = "test";
        str.getChars(0, 4, dst, 10);
        System.out.println(">>>>>Char array:" + Arrays.toString(dst));

同款异常如下:
再谈StringBuilder为什么线程不安全以及带来的问题

2.3 StringBuilder多线程不安全带来的问题?

程序无法正常执行(抛出数组越界的异常),而不是数据错乱问题。

3 小结

(1)StringBuilder的多线程不安全是指程序不能正常执行,即数组越界异常,而不是数据错乱问题。
(2)AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
(3)StringBuilde多线程不安全带来的问题是程序无法正常运行。文章来源地址https://www.toymoban.com/news/detail-502921.html

到了这里,关于再谈StringBuilder为什么线程不安全以及带来的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SimpleDateFormat为什么是线程不安全的?

    大家好,我是哪吒。 在日常开发中,Date工具类使用频率相对较高,大家通常都会这样写: 这很简单啊,有什么争议吗? 你应该听过“时区”这个名词,大家也都知道,相同时刻不同时区的时间是不一样的。 因此在使用时间时,一定要给出时区信息。 对于当前的上海时区和

    2024年02月20日
    浏览(53)
  • Java基础:为什么hashmap是线程不安全的?

    HashMap 是线程不安全的主要原因是它的内部结构和操作不是线程安全的。下面是一些导致 HashMap 线程不安全的因素: 非同步操作:HashMap 的操作不是线程同步的,也就是说,在多线程环境下同时对 HashMap 进行读写操作可能会导致数据不一致的问题。 非原子操作:HashMap 的操作

    2024年02月10日
    浏览(40)
  • Redis——关于它为什么快?使用场景?以及使用方式?为何引入多线程?

    目录 1.既然redis那么快,为什么不用它做主数据库,只用它做缓存? 2.Redis 一般在什么场合下使用?  3.redis为什么这么快? 4.Redis为什么要引入了多线程? redis设计者的初衷,就只是为了存储 小量级的共享数据 。 所以,他敢直接单线程直接干上去,因为数据量小,所以够快

    2024年01月25日
    浏览(53)
  • 【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?

    一.多线程下引起的超卖问题呈现 1.1.我先初始化库存数量为1、订单数量为0 1.2.然后我开启3个线程去执行业务 业务为:判断如果说库存数量大于0,则库存减1,订单数量加1 结果为:库存为-2,订单数量为3 原因:如下图所示,这是因为分别有6个指令(3个库存减1指令,3个订单

    2024年02月03日
    浏览(54)
  • 为什么 IDEA 建议去掉 StringBuilder,而要使用 “+” 拼接字符串?

    作者:京东零售 姜波 来源:京东云开发者社区 各位小伙伴在字符串拼接时应该都见过下面这种提示: 内容翻译:报告StringBuffer、StringBuilder或StringJoiner的任何用法,这些用法可以用单个java.lang.String串联来替换。使用字符串串联可以使代码更短、更简单。只有当得到的串联至

    2024年02月05日
    浏览(67)
  • 7.1 String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

    可变性 简单的来说: String 类中使用 final 修饰字符数组来保存字符串, private final char value[] ,所以 String 对象是不可变的。 补充(来自issue 675):在 Java 9 之后,String 、 StringBuilder 与 StringBuffer 的实现改用 byte 数组存储字符串 private final byte[] value 而 StringBuilder 与 Str

    2024年02月15日
    浏览(38)
  • 面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)

    金三银四 ? 也许,但是。 近日,又收到金三银四一线作战小队成员反馈的战况 : 我不管你从哪里看的面经,但是我不允许你看到我这篇文章之后,还不清楚这个面试问题。 本篇内容预告:   ArrayList 是线程不安全的, 为什么 ? ① 结合代码去探一探所谓的不安全  ② 我们

    2024年02月02日
    浏览(60)
  • 什么是线程?为什么需要线程?和进程的区别?

    目录 前言 一.线程是什么? 1.1.为什么需要线程 1.2线程的概念 1.3线程和进程的区别  二.线程的生命周期 三.认识多线程 总结 🎁个人主页:tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 🎥 本文由 tq02 原创,首发于 CSDN🙉 🎄 本章讲解内容: 线程的讲解 🎥学习专栏:

    2024年02月14日
    浏览(85)
  • 为什么要使用线程池

    线程池主要是 控制运行的线程的数量 ,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。 Java线程的 创建 非常昂贵,需要 JVM 和 OS (操作系统)配合

    2023年04月09日
    浏览(63)
  • 为什么使用线程池?解释下线程池参数?

    (1)降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗。 (2)提高响应速度:任务来了,直接有线程可用可执行,而不是线创建线程再执行。 (3)提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。 (1)corePoolSize:代表核心线程数,也

    2024年02月16日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包