最近在恶补知识,也是发现了,知识看一遍理解不完全,也记不住,是需要多次重复,多次熟悉,每次都有新发现。
java锁分为4中:无锁状态、偏向锁、轻量级锁、重量级锁,四种锁只能升级不能降级,竞争程度也随之升级。
1,无锁状态,顾名思义;
2,偏向锁,HotSpot的作者经过研究发现,多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得。因此为了让线程获得锁的代价降低,因此有了偏向锁。太繁琐了,简而言之,一个叫做java对象的东西,他内部有各种各样(其实就3个)的字段:
a,Mark Word(与锁、hasCode有关);
b,Class Metadata Address(存储到对象类型的数据的指针);
c,Array Length(如果对象是数组,就是数组的长度)。
java对象的Mark down里存着某个线程的id,这个是偏向锁,如果没有其他的线程来竞争,那加锁和解锁的过程只是测试一下对象头的Mark down里是否存储着该线程的偏向锁。那如果这个时候有另外的线程过来竞争了,偏向锁就要撤销了。
3,轻量级锁,如果某个对象是多个线程经常竞争的话,那就是轻量级锁了,这时候,单单靠对象头里设置一个线程的id已经无法解决问题了。这时候,需要另外一种方式:线程在栈桢中创建一块空间,把对象头的Mark down信息复制过来,官方成为Displaced Mark Down。
加锁的时候,线程尝试使用CAS将对象头的Mark Down替换为锁记录的指针,如果成功了,则就获取锁成功了,否则,就表示有其他的线程也竞争,此时,当前线程就会使用自旋来获取锁(自旋?左旋?右旋?还没来得及搞清楚,等后续搞清楚再来)。
解锁的时候,会使用原子的CAS将当前线程中的Displaced Mark Down替换回到对象的对象头里,还原成它原来的样子,如果成功,就表示竞争没有发生,如果失败,就表示当前的锁,存在着竞争,锁就会膨胀为重量级锁。一旦锁升级为重量级锁,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后,会唤醒这些线程,被唤醒的线程就会进行新一轮的夺琐之争。
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法相比,仅存在纳秒级差距 | 如果线程存在锁竞争,会带来额外的锁撤销和损耗 | 适用于只有一个线程访问同步块的场景 |
轻量级锁 | 竞争的线程不会阻塞,提高了程序的响应速度 | 如果始终得不到锁竞争的线程,使用自旋会消耗CPU | 追求响应时间,同步块执行速度非常快 |
重量级锁 | 线程竞争不使用自旋,不会消耗CPU | 线程阻塞,响应时间缓慢 | 追求吞吐量,同步块执行速度较长 |
锁,就是这些,有些能锁住人,却锁不住心;有些能锁住代码,却锁不住雪崩。
以上来自:《Java并发编程的艺术》总结