参考自美团技术团队。

一、synchronized实现同步的原理

我们需要了解两个重要的概念:“Java对象头”、“Monitor”。

1.对象头

synchronized是悲观锁,在操作同步资源之前需要给同步资源先加锁,这把锁就是存在Java对象头里。

我们以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)。

Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。

Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

2.Monitor

Monitor可以理解为一个同步工具或一种同步机制,通常被描述为一个对象。每一个Java对象就有一把看不见的锁,称为内部锁或者Monitor锁。

Monitor是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联,同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。

3.实现方式

synchronized通过Monitor来实现线程同步,Monitor是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的线程同步。

如同我们在自旋锁中提到的“阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长”。这种方式就是synchronized最初实现同步的方式,这就是JDK 6之所以synchronized效率低的原因。这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”,JDK 6中为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。

二、锁状态

1.概述

2.无锁

无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

3.偏向锁

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。

在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。

当一个线程访问同步代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID。在线程进入和退出同步块时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可。

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态。撤销偏向锁后恢复到无锁(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

**偏向锁在JDK 6及以后的JVM里是默认启用的。**可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

4.轻量级锁

轻量级锁是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁不会阻塞,从而提高性能。

在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。

拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。

如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。

如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。

若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。

5.重量级

升级为重量级锁时,锁标志的状态值变为“10”,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态

总结

简单来说:偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。而轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。重量级锁是将除了拥有锁的线程以外的线程都阻塞。

synchronized实现同步的原理和锁状态相关推荐

  1. synchronized的使用和底层原理、锁状态的膨胀升级过程

    文章目录 1. synchronized介绍 2. synchronized底层原理 3. synchronized锁的膨胀升级过程 4. synchronized锁状态的记录位置 5. synchr ...

  2. synchronized原理_synchronized关键字的作用、原理以及锁优化

    synchronized简介 synchronized块是Java提供的一种原子性内置锁,Java中的每个对象都可以隐式地扮演一个用于同步的锁的角色.这些Java内置的使用者看不到的锁被称为内部锁(i ...

  3. 存储过程没有执行完后没有释放锁_面试必问---synchronized实现原理及锁升级过程你懂吗?...

    synchronized实现原理及锁升级过程 前言: synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差 ...

  4. 在c#中用mutex类实现线程的互斥_面试官经常问的synchronized实现原理和锁升级过程,你真的了解吗...

    本篇文章主要从字节码和JVM底层来分析synchronized实现原理和锁升级过程,其中涉及到了简单认识字节码.对象内部结构以及ObjectMonitor等知识点. 阅读本文之前,如果大家对synch ...

  5. java同步锁synchronized_synchronized、锁、多线程同步的原理是咋样的?

    先综述个结论:一般说的synchronized用来做多线程同步功能,其实synchronized只是提供多线程互斥,而对象的wait()和notify()方法才提供线程的同步功能.一般说synchro ...

  6. synchronized 字节码层原理解析及锁升级

    作为Java程序员可能使用synchronized的频次不是很多大部分时间都是在crud,但是作为一个技术人还是要有点追求的,本篇文章想写了好久在此之前需要了解Java运行时数据区.Java字节码与字 ...

  7. 解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团)

    解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 参考文章: (1)解决多线程安全问题-无非两个方法synchronized和lo ...

  8. JVM系列之:对象的锁状态和同步

    文章目录 简介 java对象头 java中锁状态的变化 偏向锁biased locking 轻量级锁thin lock 重量级锁 三种锁状态的不同 简介 锁和同步是java多线程编程中非常常见的使用场 ...

  9. @Java | Thread synchronized - [ 线程同步锁 基本使用]

    对实现了Runnable或者Callable接口类,可以通过多线程执行同一实例的run或call方法,那么对于同一实例中的局部变量(非方法变量)就会有多个线程进行更改或读取,这就会导致数据不一致,sy ...

最新文章

  1. swift通知栏推送_如何使用Swift和Laravel使用推送通知创建iOS加密跟踪应用
  2. 投入千亿的菜鸟网络智慧物流做得怎么样?
  3. 1.3 Error和Exception的异同
  4. 亚马逊评价抓取插件_亚马逊运营必备插件
  5. Linux socket编程(二) 服务器与客户端的通信
  6. Chrome keyboard shortcuts
  7. debug模式不报错,release模式报错
  8. js json制表符报错_Node.js一个好汉的三个帮
  9. Mysql事务,并发问题,锁机制
  10. ISO27001的认证周期及流程
  11. springboot学习笔记1——springboot初始化
  12. iptables工作原理及iptables命令行使用介绍
  13. Date setMonth(date.getMonth - 1)
  14. c语言 dfs,DFS(深度优先搜索)
  15. Delphi 函数使用技巧
  16. 关于Scanner一些用法
  17. 数据结构(七)二叉搜索树
  18. Android Oss上传文件简单使用
  19. graphviz 文本画图工具
  20. Internet与Web技术的基本概念

热门文章

  1. pip安装gluonts
  2. 获取当地天气_6 点起来看天气预报?正经人谁看天气预报,原来是为了看她
  3. Linux CA证书与https讲解
  4. JVM笔记(一)数字在JVM中的表示
  5. ueditor chrome bug
  6. POJ3728 THE MERCHANT LCA RMQ DP
  7. 软件的极简主义的三个大敌:配置文件,冗余的参数,和大量复杂的接口。
  8. WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.
  9. linux内核剪裁 4412,itop4412开发板-Linux内核的编译
  10. mysql 多实例 ERROR 2002 ERROR 1045