相信大部分同学学过并发编程相关的课程都接触到了synchronized,它在并发编程中一直以元老级的角色存在,通过使用synchronized能够保证我们写的程序在多线程中能够按照我们的预期去执行,用过它的同学想必都知道这个关键字是很“笨重”的,非常消耗cpu资源,不过在jdk1.6中已经对它进行了优化,我们接下来开始学如何使用它以及jdk1.6中对他进行了哪些优化

synchronized 有哪几种使用方式?

  • 普通同步方法,锁是当前实例对象
  • 静态同步方法,锁是当前类的class对象
  • 同步代码块,锁事synchronized 括号中的对象

其中同步代码块是通过monitorenter和monitorexit进行实现,它们分别放在同步代码块开始和结束的位置,当然这个是编译器帮我们插入的。

jvm 怎么标识线程已经获取锁呢?这得要说说java对象头了

存储对象的hashcode或锁信息

存储到对象类型数据的指针

数组长度(如果当前对象是数组)

Mark Word

Class Metadata Address

Array length

上面就是对象头的基本结构了,锁的状态也是存储在Mark Word中,在jdk1.6中,为了减少synchronized 获得锁和释放锁带来的性能消耗,Mark Word中锁的状态由`无锁状态``偏向锁``轻量级锁``重量级锁`组成,锁的等级一级比一级高。

注意:锁可以升级但是不能降级

什么是偏向锁?


hotspot作者研究发现,锁在大多数情况下不存在竞争,并且总是被同一个线程获取,为了让线程获得锁的代价更低,于是引入了偏向锁:当一个线程获取锁并且进入了同步代码块之后,会在当前的锁对象头中记录当前线程的线程Id,后续再次进入代码块时,只需要判断锁对象头中的线程ID是否是自己,如果是的话则不再需要进行Cas加锁和解锁,否则需要重新获取。

  • 偏向锁的获取
  1. 线程A进入临界点前,判断对象头中Mark Word中是否存着指向本线程的偏向锁,如果有,则直接获取锁成功,进入临界区,如果不存在,则执行下一步
  2. Mark word的偏向锁表示位是否设置成1(当前已经是偏向锁),如果没有,则使用CAS竞争锁,否则
  3. 使用CAS将对象头的偏向锁指向当前线程
  • 偏向锁的撤销
  1. 锁撤销只会发生在锁竞争的时候并且当前没有正在执行的字节码
  2. 暂停拥有偏向锁的线程
  3. 检查拥有偏向锁的进程是否存活,如果不处于存活状态,则将对象头设置为无锁状态,否则
  4. 如果线程一直存活,则拥有偏向锁的栈会被执行,栈中的锁记录和对象头的Mark word要么重新偏向于其他线程,要么恢复到无无锁状态,最后唤醒暂停的线程
  • 关闭偏向锁
    偏向锁在jdk中默认是启用的,如果你确定你的线程中会发生锁竞争,那么你可以使用如下命令关闭偏向锁
    -XX:BiasedLockingStartupDelay = 0;

轻量级锁

  • 轻量级锁加锁
  1. 线程在执行同步代码块之前,JVM会在当前的栈桢中创建存储锁记录的空间,并将锁对象头的Mark Word内容复制到刚才创建的锁记录空间中,这一步叫做“Displaced Mark Word”,
  2. 然后当前线程尝试使用CAS将对象头中的Mark Word替换为指向当前锁记录空间的内存地址,
  3. 如果替换成功则成功获取锁,否则表示有其他线程在竞争这把锁,则
  4. 使用自旋的方式来获取锁。
  • 轻量级锁解锁
  1. 使用CAS操作将获取锁之前复制的Mark Word替换回锁对象头中,如果成果成功,则表示没有竞争发生,
  2. 如果失败,锁就会膨胀成重量级锁,并且在使用自旋方式尝试获取轻量级锁的线程会被阻塞
  3. 拥有轻量级锁的线程释放锁并唤醒等待的线程,被唤醒的线程重新争夺锁,访问临界区。

为什么锁不能被降级


因为自旋锁会消耗CPU,为了避免无用的自旋操作,特别是当持有锁的线程被阻塞了,一旦锁升级为重量级锁,就不会再恢复到轻量级锁,当锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后,会唤醒这个阻塞的线程,被唤醒的线程重新争夺锁。

锁的优缺点对比


优点

缺点

使用场景

偏向锁

加锁和解锁不需要额外的消耗

如果线程间存在竞争,则需要额外消耗

只有一个线程的场景

轻量级锁

竞争的线程不会阻塞,提高程序响应速度

自旋操作,时间过长会消耗过多的cpu

追求响应时间,同步块中的任务执行的很快,不会长时间阻塞

重量级锁

线程竞争不使用自旋,不会消耗过多cpu

线程阻塞,响应时间慢

追求吞吐量,同步块执行时间长

并发编程|连小白都能听懂的“synchronized”关键字讲解,面试官直呼好!_多线程

微信搜一搜【AI码师】关注帅气的我,回复【干货】,将会有大量面试资料和架构师必看书籍等你挑选,包括java基础、java并发、微服务、中间件等更多资料等你来取哦。
书读的越多而不加思考,你就会觉得你知道得很多;而当你读书而思考得越多的时候,你就会越清楚地看到,你知道得很少。——伏尔泰