前言:java中存在着许多的锁,比较熟悉的有偏向锁,轻量级锁,重量级锁,有些人也会把Synchronized称为重量级锁,让我们一起来学习下它们吧。
Synchronized关键字
Synchronized关键字,应该是大多数程序员最为熟悉的了,为什么呢?因为初学者都会接触到它,它可以阻塞其他线程执行该同步代码块的逻辑,所以也称它为重量级锁,它可以作用在三大地方:
(1) 作用于普通方法,锁就是当前实例对象。
/**
* 出传入的值与i进行相加,
* 不加synchronized在多线程情况下是会出现并发问题
* @param y 入参
* @return 相加结果
*/
private synchronized int inc(int y) {
return i = i + y;
}
(2) 作用于静态同步方法,锁就是当前类的Class对象。
/**
* 出传入的值与i进行相加,
* 不加synchronized在多线程情况下是会出现并发问题
* @param y 入参
* @return 相加结果
*/
private static synchronized int inc(int y) {
return i = i + y;
}
(3) 作用于同步方法块,锁就是Synchronized里配置的对象。
/**
* 出传入的值与i进行相加,
* 不加synchronized在多线程情况下是会出现并发问题
* @param y 入参
* @return 相加结果
*/
private int inc(int y) {
private synchronized int inc(int y) {
synchronized (this) {
return i = i + y;
}
}
synchronized在并发编程中属于元老级角色,在项目中synchronized关键使用得还是相对比较少(非常影响性能),从java6之后不断的synchronized进行优化,java6引入偏向锁和轻量级锁,java8有采用了分段锁和CAS,以及锁结构的升级让它变得不是那么笨重了。
偏向锁和轻量级锁的引入
频繁获取锁和释放锁是需要消耗非常多的性能,则引入偏向锁和轻量级锁,锁一共有4总状态:无锁状态、偏向锁、轻量级锁以及重量级锁,锁是可以升级,但是不可以降级的。
偏向锁
锁的获取
很多时候会存在,一个锁总是由一个线程获得,为了让获得锁所消耗的性能更低,于是引偏向锁,当线程进入同步代码块,会在对象头和栈帧中记录存储偏向锁的线程id,以后该线程进入和退出时dou都不需要进行CAS操作来加锁和解锁,只需要测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁id,如果测试成功表示,表示当前线程已经获得了锁,如果测试失败,还需要在检查一下偏向锁的标志是否设置为1(表示当前是偏向锁);如果没有设置,则使用CAS竞争者锁,如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。
锁的撤销
当出现线程竞争是偏向锁才会撤销,锁的撤销需要等待当前线程在这个时间点上没有存在正在执行的字节码文件,当锁存在一直竞争时,就会升级为轻量级锁。
轻量级锁
线程在进入同步代码块之前,JVM会在当前线程栈帧中创建存储记录锁的空间,并将对象头复制到锁记录中,线程使用CAS将对象头的Mark Word替换为锁记录的指针,如果失败,将将会使用自旋来获取锁,解锁时,使用CAS操作替换回去,如果失败,表示存在竞争,锁也随之将升级为重量级锁。