Java高并发系列 — 锁

Java都有哪些锁?

  synchronized 和 reentranlock是最常见的,其中前者又JVM提供实现,后者有专门对应的java.util.concurrent包提供;同时后者功能更加丰富、灵活

悲观锁和乐观锁

  悲观锁认为,每一次走进同步代码都可能发生线程安全问题,因此只要触及代码块都会加锁,而乐观锁则认为大部分情况都不会出现线程安全问题,所以只要出现问题的时候再自旋CAS

可重入锁和不可重入锁

  不可重入锁,就是某线程获取该锁但未释放的时候,如果再次获取该锁,则只能等待;而可重入锁不需要等待,只需要为锁数量加1;其中注意,synchronized和reentrantlock都是可重入锁

Synchronized

  JVM实现的锁,实现原理是利用操作系统的内核态的Mutex互斥量实现的,因为涉及内核态的调用所以一般认为是重量级锁,但后面已经被官方优化过,所以很难说synchronized就效率很低,优化的主要思想还是乐观锁,优化方案有:适应性自旋锁、锁消除、锁粗化、轻量级锁、偏向锁

  【自旋锁】自旋锁(Spin Lock),在一个线程进入synchronized代码块,获取锁的时候不直接进入ContetionList(之前已经有线程)等候,而是先自旋等待一下,期待Owner线程马上释放锁;这样虽然对ContetionList里面的线程不太公平但是总体效率能提高;因为线程进入ContetionList阻塞,需要进入内核调度状态,非常耗时;

  【偏向锁】(Biased Lock),我们写的代码,很多时候也有少有竞争的情况,而锁经常都是可重入的,而偏向锁就是解决这类场景的优化,轻量级锁就是为了在无多线程竞争的环境中使用CAS来代替mutex,一旦发生竞争,两条以上线程争用一个锁就会膨胀。然后就会升级为轻量级锁。

  要知道,sync是基于对象头的监视器的,所以结合对象头的知识,自旋锁,偏向锁工作如下:

每一个线程在准备获取共享资源时: 
第一步,检查MarkWord里面是不是放的自己的ThreadId ,如果是,表示当前线程是处于 “偏向锁” 
第二步,如果MarkWord不是自己的ThreadId,锁升级,这时候,用CAS来执行切换,新的线程根据MarkWord里面现有的ThreadId,通知之前线程暂停,
之前线程将Markword的内容置为空。 
第三步,两个线程都把对象的HashCode复制到自己新建的用于存储锁的记录空间,接着开始通过CAS操作,
把共享对象的MarKword的内容修改为自己新建的记录空间的地址的方式竞争MarkWord, 
第四步,第三步中成功执行CAS的获得资源,失败的则进入自旋 
第五步,自旋的线程在自旋过程中,成功获得资源(即之前获的资源的线程执行完成并释放了共享资源),则整个状态依然处于 轻量级锁的状态,如果自旋失败 
第六步,进入重量级锁的状态,这个时候,自旋的线程进行阻塞,等待之前线程执行完成并唤醒自己

  

Renentrantlock

  J.U.C提供的锁,实现方式是利用volatile和cas自旋的方式实现,主要核心组件是AQS,该知识点请参见我的另一篇参见AQS的文章

    原文作者:饭小胖
    原文地址: http://www.cnblogs.com/iCanhua/p/9250604.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞