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

news2024/11/19 11:29:50

1 缘起

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

2 StringBuilder

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

位置:java.lang.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出现数组越界,无法正常添加数据,程序异常。
异常信息如下图所示:
在这里插入图片描述

2.2 为什么线程不安全?

结论:因为AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
先从append方法说起,StringBuilder继承AbstractStringBuilder,而StringBuilder中直接使用了父类的append方法。
在这里插入图片描述
既然StringBuilder直接使用了AbstractStringBuilder的append方法,我们直接探究这个父类的方法,源码如下,使用的全局变量count作为数组扩容的参数:ensureCapacityInternal。

在这里插入图片描述
数组扩容系列操作源码如下,如果传入的值大于存储数据的字符数组长度,则拷贝数据到新的数组中,保证数据可以正常存储。
问题就出现在这里,多线程出现数组没有及时扩充,使用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开始,因此,无法再写入数据了,抛出异常。

在这里插入图片描述

在这里插入图片描述
多线程出现的时间差,导致数组没有正常被扩容,
最后无法写入数据,数组越界后抛出异常。
测试样例:

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

同款异常如下:
在这里插入图片描述

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

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

3 小结

(1)StringBuilder的多线程不安全是指程序不能正常执行,即数组越界异常,而不是数据错乱问题。
(2)AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
(3)StringBuilde多线程不安全带来的问题是程序无法正常运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/681328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无标题】TP-LINK XDR5470 WiFi6路由器 简单开箱评测

TL-XDR5470易展版AX5400双频WiFi6路由器 简单开箱测评&#xff0c;上次买的XDR6078覆盖不够&#xff0c;还是得每层再买一个&#xff0c;所以又买了个TL-XDR5470&#xff0c;支持易展mesh。 上次买的XDR6078没有外置FEM功放芯片&#xff0c;所以信号差了一点&#xff0c;得加2…

PE系统盘制作

目录 前言 制作PE盘的步骤如下 前言 PE盘是一个轻量级的系统&#xff0c;类似于Windows系统。当您的计算机无法进入Windows系统时&#xff0c;您可以通过启动PE盘来访问一个独立的操作系统&#xff0c;从而执行各种任务&#xff0c;例如拷贝重要文件或进行系统安装。PE盘通常…

win10查看端口是否被占用,被哪一个程序占用(图文)

window系统中有时候我们会出现需要的端口号被占用&#xff0c;但不知道具体是哪个程序占用的。这时我们需要找到使用此端口的程序。 方法如下&#xff1a; 1&#xff09;以管理员身份打开命令提示符窗口&#xff08;开始-运行&#xff09;。 2&#xff09;使用命令查看端口使…

R730直通Tesla P40显卡

本次讲述如何在R730的ESXi上&#xff0c;将Tesla P40直通到centos7.7和WinServer2016。使用直通模式&#xff0c;安装普通的驱动即可&#xff0c;不需要vGPU的驱动。 按计划本来后面要自己装一下系统、做RAID的&#xff0c;不过最近需要用到显卡&#xff0c;所以先把显卡安装上…

初探Flink的Java实现流处理和批处理

端午假期&#xff0c;夏日炎炎&#xff0c;温度连续40度以上&#xff0c;在家学习Flink相关知识&#xff0c;记录下来&#xff0c;方便备查。 开发工具&#xff1a;IntelliJ Idea Flink版本&#xff1a;1.13.0 本次主要用Flink实现批处理&#xff08;DataSet API&#xff09; 和…

SAM与Prompt的结合

1. SAM介绍 由Meta AI Research开发的Segment anything model&#xff08;简称SAM&#xff09;最近引起了广泛的关注。SAM在超过10亿个mask的大型分割数据集上进行了训练&#xff0c;能够在特定的图像上分割任何对象。在最初的SAM工作中&#xff0c;作者们使用了零样本迁移任务…

08- c语言字符串 (C语言)

一 字符串的定义及基本使用 1、什么是字符串 被双引号引用的字符集合&#xff01;例如&#xff1a;”hello” 、”world”&#xff0c;或者是以 \0 结尾的字符数组&#xff01;&#xff01;&#xff01; 比如&#xff1a;char ch[] {h, e, \0} 注意&#xff1a;”hello” 中…

GB50149-2010电气装置安装工程母线装置施工及验收规范

为了确保强硬钢丝绳金属封闭体绝缘金属封闭母线、绝缘子、硬件、穿墙套管等设备母线的安装质量,加快安装技术的进步,和确保设备的安全运行,使该规范。 本规范适用于总线设备安装了750 kv及以下的T范围施工和验收。 母线的安装应按照批准的设计文件施工。 设备和设备运输、储…

python:并发编程(二十五)

前言 本文将和大家一起探讨python并发编程的实际项目&#xff1a;win图形界面应用&#xff08;篇七&#xff0c;共八篇&#xff09;&#xff0c;系列文章将会从零开始构建项目&#xff0c;并逐渐完善项目&#xff0c;最终将项目打造成适用于高并发场景的应用。 本文为python并…

postman 文档、导出json脚本 导出响应数据 response ,showdoc导入postman json脚本 导出为文档word或markdown

生成文档 Collections中 选中文件夹 - ... (文件夹 功能小按钮) - view documentation : 保存响应数据 Response&#xff1a;&#xff08;如果导出接口数据&#xff0c;会同步导出响应数据&#xff09; 请求接口后&#xff0c;点击下方 Save as Example 可以保存响应数…

机器学习day20(前向传播的向量化代码,矩阵乘法)

前向传播的循环代码与向量化代码的对比 把X、B写作二维数组&#xff0c;即矩阵左边的for循环就可以用右边的np.matmul来实现matmul是numpy执行矩阵乘法的一种方式注意&#xff1a;此时所有的变量&#xff08;X、W、B、Z、A_out&#xff09;都是二维数组&#xff0c;即矩阵 向…

基础排序算法【归并排序+非递归版本+边界修正】

基础排序算法【归并排序非递归版本边界修正】 Ⅰ.归并排序(递归版本)①.分割②.归并③.拷贝 Ⅱ.非递归版本Ⅲ.边界修正 Ⅰ.归并排序(递归版本) 递归排序&#xff0c;采用的是分治法。分成子问题来处理。先让序列不断分割成子序列&#xff0c;当子序列有序后再合并。 对于一段…

Yolov5-Face 原理解析及算法解析

YOLOv5-Face 文章目录 YOLOv5-Face1. 为什么人脸检测 一般检测&#xff1f;1.1 YOLOv5Face人脸检测1.2 YOLOv5Face Landmark 2.YOLOv5Face的设计目标和主要贡献2.1 设计目标2.2 主要贡献 3. YOLOv5Face架构3.1 模型架构3.1.1 模型示意图3.1.2 CBS模块3.1.3 Head输出3.1.4 stem…

202320读书笔记|《宋词》——竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生

202320读书笔记&#xff5c;《宋词》——竹杖芒鞋轻胜马&#xff0c;谁怕&#xff1f;一蓑烟雨任平生 《宋词》韩震主编&#xff0c;偶然从书友那加入书架的书。宋词挺喜欢李清照的词以及知否的《菩萨蛮》。诗集&#xff0c;词&#xff0c;俳句&#xff0c;短歌我都很喜欢&…

工欲善其事,必先利其器-基于ubuntu18.04 VScode开发100ASK-ESP32

点击上方“嵌入式应用研究院”&#xff0c;选择“置顶/星标公众号” 干货福利&#xff0c;第一时间送达&#xff01; 来源 | 嵌入式应用研究院 整理&排版 | 嵌入式应用研究院 前面我们基于ubuntu环境搭建了esp-idf的开发环境&#xff0c;它也是为了接下来基于VSCode来开发1…

【openGauss基本概念】---快速入门

【openGauss简单使用】---快速入门 &#x1f53b; 一、基本概念&#x1f530; 1.1 openGauss&#x1f530; 1.2 数据库&#xff08;Database&#xff09;&#x1f530; 1.3 数据块&#xff08;Block&#xff09;&#x1f530; 1.4 行&#xff08;Row&#xff09;&#x1f530; …

【MySQL数据库】主从复制与读写分离

目录 一、读写分离1.1概述1.2为什么要读写分离呢&#xff1f;1.3什么时候要读写分离&#xff1f; 1.4主从复制与读写分离1.5mtsql支持的复制类型1.6主从复制工作流程1.7主从复制原理 二、主从复制实战 一、读写分离 1.1概述 读写分离&#xff0c;基本的原理是让主数据库处理事…

React从入门到实战 -组件的三大核心属性(2)props

文章目录 基本使用对props进行限制类式组件中的构造器 基本使用 // 定义组件class MyComponent extends React.Component{render(){return (<ul><li>姓名&#xff1a;{this.props.name}</li><li>年龄&#xff1a;{this.props.age}</li><li>…

【开源与项目实战:开源实战】85 | 开源实战四(中):剖析Spring框架中用来支持扩展的两种设计模式

上一节课中&#xff0c;我们学习了 Spring 框架背后蕴藏的一些经典设计思想&#xff0c;比如约定优于配置、低侵入松耦合、模块化轻量级等等。我们可以将这些设计思想借鉴到其他框架开发中&#xff0c;在大的设计层面提高框架的代码质量。这也是我们在专栏中讲解这部分内容的原…

MongoDB负载均衡集群(第8章节选)

MongoDB自身可组成分片加复制的集群&#xff0c;在这个集群的前端加上负载均衡器&#xff08;比如HAProxmy Keepalived&#xff09;&#xff0c;就可组建成一个无单点故障、十分完美的高可用负载均衡集群&#xff08;如图8-1所示&#xff09;。 图8- 1 整个MongDB高可用体系结…