点击上面↑「爱开发」关注我们
分享软件开发资源、社交电商资源
分享职场干货、软件编程、程序人生和创业资源。
文|洪生鹏 编辑|静子
程序员面试时,线程安全知识点经常会遇到。
小洪是一名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不是线程安全的例子就分析到这。笔者觉得类似这样的基础知识点需要在平时学习中不断摸索,不断去探索实践,不断运用,才能在工作中更好地解决问题。由于笔者水平有限,文中纰漏之处在所难免,权当抛砖引玉,不妥之处,敬请读者不吝赐教,是为至盼。
作者:洪生鹏 微信公众号:洪生鹏 头条号:洪生鹏 个人微信号:hsp-88ios
往期精选推荐
闲聊区
同事辞职了,我一个人做两个人的活,公司又不招人了,该怎么办?
育儿区
孩子不吃饭,让她饿一顿就好了?医生的话让妈妈很后悔,家长们可要留意了