Synchronized下的自旋锁、偏向锁、轻量级锁以及重量级锁(这是一篇有味道的博客)

在理解Synchronized中的锁时,需要先了解一下CAS,CAS在应用中有很多的用途。

什么是CAS?

CAS的全名叫做Compare and Swap,翻译过来就是先比较再交换,在JAVA的并发包中很多的类用到了这个技术,也和数据库的乐观锁机制是一样的。

举个栗子说明下

      1。第一步拿到需要修改的对象

      2。第二步修改当前对象的值

      3。将当前的对象放回引用地址,如果数据未发生改变,那么修改成功,否则就修改失败。

  众所周知,CAS有一个关键性的ABA问题。

       A——B——A  第一个线程获取到数据A,中途有第二个线程加入,修改了A的数据为B,第三个线程加入又将数据修改回了A。当第一个线程触发CAS操作时,由于当前的A仍然等于A,所以默认数据是未发生改变的,结果会造成数据上的偏差。

    而目前JAVA给出了2个解决方案,分别是AtomicStampedReference /AtomicMarkableReference,其实现原理和乐观锁的机制相同,通过给数据的操作加上版本号,来解决数据发生改变时,第一个线程获取到的还是原来值的问题。

   但是就上述两种而言,前者是根源解决了该问题,是用版本号控制,而后者只是用Boolean值进行了解决,只是降低了ABA发生问题的可能性而已。

   前者的机制类似于乐观锁

   1A——2B——3A   即使数据仍然是A,但是最后一步A的版本号为3,1A≠3A,因此可以避免ABA的问题(《JAVA并发编程的艺术》第一章的末尾有具体的解释,大家可以反复阅读这部分)

 

自旋锁:自旋锁其实和互斥锁是对立的关系。

以下摘自百度百科:

对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

其实说到底就是有一个线程来获取锁,但是由于锁已经被占用,那么就重复向锁发出请求,是否可以拿到锁,直到拿到锁或者达到设定自旋的时间或者达到自旋的次数才会停止,所以自旋锁如果在一直拿不到锁的情况非常耗损CPU

 

偏向锁:其实就是偏向于一个线程的锁。在线程运行时,会给当前锁的标志位(俗说的markdown)给定一个当前线程的标志,而当线程第二次进来的时候通过CAS发现标志位相同,这种情况下就会触发偏向锁。

因为需要CAS比较标志位,所以偏向锁适用于单线程情况,不需要通过自旋锁,因此效率很高。

 

 

轻量级锁:线程最先开始的状态是偏向锁,当有第二个线程过来竞争锁时,通过CAS发现当前的标志位并不是自己,于是需要竞争锁,通过CAS进行修改标志位,如果修改成功,则偏向锁升级为轻量级锁;如果修改失败,自旋获取该锁,如果自旋失败达到指定次数或者达到指定时间阈值,为了CPU的性能考虑,那么该线程会停止自旋,说明有多个线程在争抢锁,轻量级锁就要升级为重量级锁。

轻量级锁适用于逻辑简单,运行速度比较快的并且线程不是很多的情况下。

 

重量级锁:顾名思义,最重的锁了。他的好处在于其他线程不会像轻量级锁一样使用自旋的方式去获取锁,当前情况为重量级锁时,除开正在运行的线程,其他线程将进入阻塞状态,等到重量级锁释放时,会唤醒阻塞的线程进行锁的竞争。

重量级锁适用于同步代码块时间较长,逻辑较为复杂的情况

 

 

举一个生动形象的栗子帮大家理清这里面的逻辑

     获取偏向锁:       小明去上厕所,小明问了一句里面,有人嘛?没人回应(CAS),于是小明进去上了厕所,并在厕所门上标注:“小明”;

    拥有偏向锁:       小明过了一会又来了,发现厕所门上的纸还在,还是“小明”(CAS),于是小明这次没有问,直接去上厕所了;

    升级轻量级锁:   小明在上厕所的时候小华来了,小华看到门上有“小明”的字样,知道小明在里面上厕所,于是催促小明说你快点,我在外面等你呢,每隔一段时间小华就要问一次(自旋锁)。等到小明上完厕所出来,小华进去上了厕所,临上厕所之前还不忘记把厕所上的字改成了“小华”

   升级重量级锁:   小华在里面上厕所的时候,这时候来了很多的人,这些人就在门口每隔一段时间问小华什么时候好,然而经过了很多次以后小华说你们等吧,我出来了叫你们。众人说那就算了,在厕所外面安静的等吧(停止自旋,进入阻塞状态)。当小华上完厕所出来以后告诉大家,我上完了,然后众人靠自己的手速抢位置,并不是第一个人就能抢到,完全是随机性的(非公平锁)

 

提及到了Synchronized的非公平性,顺带说下一个可设置为公平的ReentrantLock的锁,ReentrantLock在构造函数创建的时候,传true即为公平锁,false为非公平锁。默认为非公平锁。

 

经常提及Synchronized和ReentrantLock的取舍,以前在一个慕课网的视频中看到过,如果在两者之间进行取舍。

1。尽量选用的JAVA的并发包中的资源,因为JAVA并发包的资源是经过许多次的测试,因此是最权威的;

2。如果Synchronized目前所拥有的特性能够解决问题的情况下,那么优先使用Synchronized;

3。如果需要使用ReentrantLock的特性,譬如公平性,定时解锁等特性的话,那么可以考虑用ReentrantLock。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值