synchronized锁升级

volatile区别在jdk1.5版本(包含)之前,锁的状态只有两种状态:无锁状态和重量级锁状态,只要有线程访问共享资源对象,则锁直接成为重量级锁,jdk1.6版本后,对synchronized锁进行了优化,新加了“偏向锁”和“轻量级锁”,用来减少上下文的切换以提高性能,所以锁就有了4种状态。注意:无锁到偏向锁这不是升级,是在偏向锁打开后,,没有从无锁升级到偏向锁的过程。偏向锁未开启,会直接从无锁升级到轻量级锁,偏向锁开启时,会从偏向锁升级到轻量级锁。

在 jdk1.5 版本(包含)之前,锁的状态只有两种状态:无锁状态和重量级锁状态,只要有线程访问共享资源对象,则锁直接成为重量级锁,jdk1.6 版本后,对 synchronized 锁进行了优化,新加了"偏向锁"和"轻量级锁",用来减少上下文的切换以提高性能,所以锁就有了 4 种状态。

  1. 无锁
    对于共享资源,不涉及多线程的竞争访问。在 DK1.6 之后偏向锁的默认开启的,但是有一个偏向延迟,需要在 VM 启动之后的多少秒之后才能开启,这个可以通过 VM 参数进行设置,同时是否开启偏向锁也可以通过 VM 参数设置。
  2. 偏向锁
    共享资源首次被访问时 ,JVM 会对该共享资源对象做一些设置,比如将对象头中是否偏向锁标志位置为 1,对象头中的线程 ID 设置为当前线程 ID(注意:这里是操作系统的线程 ID),后续当前线程再次访问这个共享资源时,会根据偏向锁标识跟线程 ID 进行比对是否相同,比对成功则直接获取到锁(锁偏向于这个线程),进入临界区域 (就是被锁保护,线程间只能串行访问的代码),这也是synchronized 锁的可重入功能
  3. 轻量级锁
    当多个线程同时申请共享资源锁的访问时,这就产生了竞争,JVM 会先尝试使用轻量级锁,以CAS 方式 来获取锁(一般就是自旋加锁,不阻塞线程采用循环等待 的方式),成功则获取到锁,状态为轻量级锁,轻量级锁竞争失败(达到一定的自旋次数还未成功)则锁升级到重量级锁。
  4. 重量级锁
    如果共享资源锁已经被某个线程持有,此时是偏向锁状态,**未释放锁前,再有其他线程来竞争时,则会升级到重量级锁,把竞争线程挂起,**重量级锁由操作系统来实现,所以性能消耗相对较高。

synchronized 和 volatile 区别

synchronized 关键字和 volatile 关键字是两个互补的而非对立的

  • volatile 关键字是线程同步的轻量级 实现,所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量synchronized 关键字可以修饰方法以及代码块 。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性synchronized 关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,同时防止指令重排序。而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

底层原理

底层实现涉及对象头、监视器锁,每个 java 对象都有一个对象头,包含锁的信息,对象头通常分为两部分:

  • Mark Word(标记字段):用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID 等。
  • Klass Pointer(类型指针):指向对象的类元数据,用于确定对象是哪个类的实例。
  • synchronized 的核心是基于 JVM 的内部数据结构 Monitor,每个对象都有一个与之关联的 Monitor,当线程获取对象的 synchronized 锁时,实际上是在获取该对象的 Monitor。
  • 当线程执行到 synchronized 时,先检查锁状态:线程首先检查对象头中的锁状态标志,然后尝试获取锁,执行同步代码块,释放锁。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计