synchronized 学习,偏向锁,轻量级锁,重量级锁介绍

Synchronized

1 加锁方式

  • 修饰实例方法 -- > 锁的是当前实例
  • 修饰静态方法 -- > 锁的是当前类 例如Demo.class
  • 修饰代码块 -- > 看具体锁对象分析

2 锁的存储

    private static  Object obj = new Object();
    public void add() {
        synchronized (obj){
            count++;
        }
    }
    //分析,synchronized(lock)是基于lock这个对象的生命周期来控制粒度的,所以所得存储应该和lock对象有关,探究jvm源码得知,当某个对象被synchronized当成同步锁时,那么围绕这个锁的一系列操作都和Mark Word有关系。
  • 以32位虚拟机为例

为什么任何对象都可以实现锁?

每个Object在jvm都有一个native的C++对象进行对应,线程在获取锁的时候,实际上就是获得一个monitor(监视器)对象,monitor可以认为是一个同步对象,所有的java对象天生都携带monitor,多个线程访问同步代码块时,其实相当于去争抢监视器对象并修改对象中的锁标记

3 锁升级

3.1 偏向锁的获取和撤销

hotspot虚拟机的作者调查发现,大部分情况下,加锁的程序不仅仅不存在线程竞争,而且总是由同一个线程获得锁,基于这样一个概率,synchronized在jdk1.6之后做了一些优化,引入了偏向锁和轻量级锁的概念

  • 基本原理

在线程访问同步代码块的时候,会在对象头中存储当前线程的ID,后续这个线程进入和退出这个代码块的时候,不需要再次加锁和释放锁,而是直接比较对象头里面是否存储了指向当前线程的偏向锁,如果相等表示锁是偏向当前线程的,没必要再次进行获得锁

  • 执行步骤

  • 偏向锁的撤销

    偏向锁不存在撤销,如果cas失败,则会升级轻量级锁

3.2 轻量级锁

  • 基本原理

偏向锁升级为轻量级锁时,对象的markword也会进行相应的变化,升级过程如下

(1) 线程在自己的栈桢中创建锁记录 LockRecord

(2) 将锁对象的对象头中的MarkWord复制到线程的刚刚创建的锁记录中

(3) 将锁记录中的 Owner 指针指向锁对象

(4) 将锁对象的对象头的 MarkWord替换为指向锁记录的指针。

  • 执行步骤

轻量级锁加锁过程中用到了自旋,默认自旋10次,可以通过 PreBlockSpin 修改, jdk1.6之后引入了自适应自旋锁

3.3 重量级锁

当轻量级锁膨胀为重量级锁后,意味着线程只能被挂起阻塞来等待被唤醒了

任意线程对 Object(Object 由 synchronized 保护)的访问,首先要获得 Object 的监视器。如果获取失败,线程进入同步队列,线程状态变为 BLOCKED。当访问 Object 的前驱(获得了锁的线程)释放了锁,然后会唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。

  • 执行步骤

4 线程间通信

原文地址:https://www.cnblogs.com/gaojf/p/12721666.html