jdk1.6中为了减少获取锁和得到锁带来的性能消耗,引入了偏向锁和轻量级锁。再java1.6中,锁有四种状态。
- 无锁
- 偏向锁
- 轻量级锁
- 重量级锁
偏向锁(只适用于一个线程访问的情景)
其实锁是不存在多线程竞争的,且总是由同一线程多次获得,为了让线程获得锁的代价更低。引入了偏向锁。当一个线程访问同步代码块并获取锁时,会在对象头和栈帧中锁记录中记录偏向锁的线程id。 当以后该线程再进入同步代码块的时候,只需要简单的测试对象头里面的Mark Word的存储结构是否存储着指向当前线程的偏向锁。如果,测试成功,那么久表示获得了锁。如果测试失败,那么就需要测试一下偏向锁的标识是否设置了1,如果是1则尝试使用CAS将对象头的偏向锁指向当前线程,如果不是1则使用CAS竞争锁。
偏向锁使用了竞争出现才释放锁的机制,当其他线程尝试晶振偏向锁时持有偏向锁的线程菜会释放锁。而且需要等待全局安全点。安全点指的是,在这个时间点上没有正在执行的字节码。撤销的时候先检查持有偏向锁的线程是否还活着,如果线程不处于活动状态,则将对象头设置为成无锁状态。如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向其他线程,要么恢复到无所或标记对象不适合作为偏向锁,最后唤醒。
轻量级锁
加锁:
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
解锁:
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成
功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。图2-2是
两个线程同时争夺锁,导致锁膨胀的流程图。
偏向锁:加锁和解锁不需要额外的消耗,但是线程之间存在锁竞争,锁撤销的时候消耗要大。
轻量级锁:竞争的线程不会阻塞,提高了线程的响应速度。如果始终得不到锁,那么就会自旋消耗cpu。 目的是为了追求相应时间。
重量级锁:不会使用自旋,不消耗cpu。线程阻塞,响应慢,但是追求吞吐量。
参考java编程之美 ,记录本文是为了加深自己对并发的锁的印象