synchronized锁
几种使用方式
-
直接往方法上加synchronized关键字:
//方法上加锁,锁的是当前对象。 //如果在静态方法上加锁,锁的是类,也就是class对象 public synchronized void temp(){ } -
同步代码块:
//可以使用对象也可以使用类的.class对象作为监视器 synchronized (MyTread.class){ System.out.println("hello"); }
锁升级
对象头
synchronized加锁其实就是给一个对象加锁,锁存在对象头中,不同线程争抢一个对象的对象头中的锁。
对象头结构:
而锁信息又存放在对象头中的mark word中。
mark word结构:
锁升级步骤
无锁========>偏向锁========>轻量级锁========>重量级锁
锁升级详细过程
偏向锁:
- 判断对象头中Mark Word的Thread Id是否为空
- 若为空cas设置Thread Id为当前线程
- 若不为空,检查Thread Id是否为当前线程Id
- 如果是当前线程,拿到锁直接执行同步代码块
- 如果失败则检查偏向锁的标识是否为1,如果不是CAS竞争锁
- 如果有偏向锁则用CAS尝试替换Mark Word中的线程Id,进行偏向锁撤销
偏向锁撤销:
- 需要等待到全局安全点(在这个时间点上没有执行的字节码文件)
- 会短暂暂停拥有偏向锁的线程,判断持有锁的线程是否存活,如果线程不在活动状态,则将对象头设置为无锁状态
- 如果持有锁的线程存活,对象头锁标记位设置为轻量级锁,唤醒持有锁的线程
轻量级锁加锁:
- 在线程执行同步代码块之前,JVM会在当前线程的栈帧中创建一个储存锁记录的空间lock record,将对象头中的Mark Word复制到lock record中,官方称为Displaced Mark Word。
- 当前线程会尝试使用CAS将Mark Word中的指针指向lock record,如果成功则获得锁,如果失败表示有其他线程竞争,会通过自旋来获得锁
轻量级锁解锁:
轻量级锁解锁时会将lock record替换回对象头,如果成功则表示没有竞争发生,如果失败则轻量级锁会膨胀为重量级锁。
由于自旋锁长时间没有获取到锁的话会消耗cpu资源,为了避免无用的自选,锁一旦升级为重量级锁之后,不会恢复到轻量级锁。
进入重量级锁之后,一旦第一个线程获得锁之后,其他线程尝试获得锁就会进入阻塞状态,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程会重新竞争锁。