单线程下StringBuffer与StringBuilder有区别吗

每当面试官问StringBuffer与StringBuilder的区别时,我们都会说StringBuffer是是线程安全的,因为StringBuffer的大部分方法都被synchronized修饰,StringBuilder是线程不安全的。StringBuilder效率更高,单线程下用StringBuilder,多线程下用StringBuffer。

 

但是面试官下个问题问“单线程下StringBuffer与StringBuilder有区别吗?”,我想到既然一个有synchronized修饰,一个没有,肯定是有区别的,用synchronized就要加锁,获取锁和释放锁都需要时间。但是如果这样答就too yong too simple了。如果面试官问你的是“单线程和多线程下StringBuffer的区别呢?”

 

其实这个问题面试官想要考察的是synchronized的四种锁的状态。级别从低到高依次是无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态。先了解一个额外知识。

Java对象头

synchroized用的锁是存在Java对象头里的。Java对象头有一部分存储了对象的HashCode,分代年龄和锁标记位,这部分叫做Mark Word。在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。下图是32位虚拟机中Mark Word可能存储的四种数据。

![5c9b782cefc7d](https://i.loli.net/2019/03/27/5c9b782cefc7d.jpg)5c9b782cefc7d

无锁状态

无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

 

无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

 

偏向锁状态

在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。

 

当一个线程访问同步代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID,可以看到上图中是用前23位来存。在线程进入和退出同步块时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。

 

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态。撤销偏向锁后恢复到无锁(锁标志位为“01”)或轻量级锁(锁标志位为“00”)的状态。

 

偏向锁在JDK 1.6及以后的JVM里是默认启用的。可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

 

 轻量级锁

当锁是偏向锁的时候,又有第二个线程来访问同步代码块,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。

 

在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”,上图没有画出来),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。

 

如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。

 

若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。

 

重量级锁

升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。

整体的锁状态升级流程如下:

![锁升级](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/8afdf6f2.png)锁升级

 

偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。而轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。重量级锁是将除了拥有锁的线程以外的线程都阻塞

 

所以这个问题只要提到JDK1.6及以后,单线程下synchronized默认加的是偏向锁,再解释一下偏向锁,锁升级就完美了。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值