面试官:连StringBuilder线程是否安全都说不出个所以然,怎么写出高效稳定的程序...

点击上面↑「爱开发」关注我们

分享软件开发资源、社交电商资源

分享职场干货、软件编程、程序人生和创业资源。

文|洪生鹏 编辑|静子

程序员面试时,线程安全知识点经常会遇到。

小洪是一名java程序员,工作两年多了,最近去了某互联网公司面试,面试时,面试官问了这样一个问题:

面试官:StringBuilder和StringBuffer有什么区别?

小洪:StringBuilder线程不安全的,StringBuffer是线程安全的

面试官:能举个例子说说StringBuilder是怎么线程不安全的吗?

小洪:……

小洪有点懵了,平时在用在使用StringBuilder追加字符时,只是简单的用,只知道它是不是线程安全,至于不安全在哪里,小洪并没有去关注。

见小洪没有作答,面谈时面试官又问,StringBuffer哪里安全了,小洪也回答不出个所以然。

面试官:你都工作两年了,这个都不知道,怎么写出稳定的程序啊?小洪一脸的无助,不过确实不应该,类似StringBuilder常见的知识点,平时应该不会少用,应该掌握才对。

刚毕业的时候,面试时也遇到了类似关于StringBuild的问题,关于StringBuild线程不安全简单来说StringBuilder没有加锁,而StringBuffer有加锁。面试时,这样回答固然没有错,但要是能进一步分析个所以然那就更好了,相信能给面试官留下更好的印象。

我们举个例子来验证下

1. 分别用500个线程写StringBuffer和StringBuilder,

2. 保证在各自500个线程执行完后打印StringBuffer和StringBuilder字符串长度

3. 检验运行结果。

测试

 1  public static void main(String[] args) {
2    StringBuilder stringBuilder = new StringBuilder();
3    class MyRunnable implements Runnable{
4        @Override
5        public void run() {
6            stringBuilder.append("p");
7        }
8    }
9    ExecutorService exec = Executors.newCachedThreadPool();
10    for(int i=0;i<500;i++) {
11        exec.execute(new MyRunnable());
12    }
13    exec.shutdown();
14    System.out.println(stringBuilder.length());
15}

我们创建了500个线程,每个线程往StringBuilder对象里面append一个字符p。正常情况下代码应该输出500,但是实际运行结果是什么呢?

测试结果

我们看到字符串长度有时候是496,有时候是498,小于预期值500。而当我们把StringBuilder换成StringBuffer不管运行多少次,结果的值都是500,从这个例子我们可以得出StringBuffer线程安全,StringBuilder线程不安全得到了证明。

为什么输出值跟预期值不一样,源码分析

为什么会小于期望值,我们来看下StringBuilder的源码

StringBuilder的append()方法调用的父类AbstractStringBuilder的append()方法,我们来看下这个方法具体做了什么。

1public AbstractStringBuilder append(String str) {
2    if (str == null)
3        return appendNull();
4    int len = str.length();
5    ensureCapacityInternal(count + len);
6    str.getChars(0, len, value, count);
7    count += len;
8    return this;
9}

细心的你会发现count += len;这一行代码并不是一个原子操作。

假设count的值为10,len值为1,这时候有两个线程同时执行到了这行代码,count值都是10,执行完后将结果赋值给count,这两个线程执行完后count值为11,而不是12。这就是为什么测试代码输出的值要小于我们的期望值500的原因了。

而当我们换成StringBuffer类是,为什么能保证输出的是500,我们看一下StringBuffer源码就会发现所有写操作都有synchronized修饰了

而StringBuilder的写操作则没有使用synchronized进行修饰。

StringBuilder类的append方法

1@Override
2public StringBuilder append(String str) {
3   super.append(str);
4   return this;
5 }

StringBuffer 类的append方法

1    @Override
2    public synchronized StringBuffer append(String str) {
3     toStringCache = null;
4     super.append(str);
5     return this;
6   }

关于StringBuilder不是线程安全的例子就分析到这。笔者觉得类似这样的基础知识点需要在平时学习中不断摸索,不断去探索实践,不断运用,才能在工作中更好地解决问题。由于笔者水平有限,文中纰漏之处在所难免,权当抛砖引玉,不妥之处,敬请读者不吝赐教,是为至盼。

-END-
作者:洪生鹏 
微信公众号:洪生鹏   
头条号:洪生鹏
个人微信号:hsp-88ios

往期精选推荐

闲聊区

同事辞职了,我一个人做两个人的活,公司又不招人了,该怎么办?

程序员月薪多少才不会焦虑

《色戒》被禁的三段激情床戏

育儿区

为什么宝宝做错事被骂哭后,还要求抱抱?家长早知道早受益

孩子被人欺负了,要不要打回去?非常赞同这位宝妈的做法

孩子不吃饭,让她饿一顿就好了?医生的话让妈妈很后悔,家长们可要留意了

更关注精彩内容,请长按识别关注
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值