什么是锁?

java中,synchronized永远都是锁定的一个对象,那么jvm是怎么判断一个对象是被锁定的呢。

java的对象内存分布

Java的对象由对象头,对象体和填充空间(Padding)组成。

  • 对象头

    对象的描述信息
  • 实例数据

    对象的实际内容
  • 填充空间

    JVM要求对象的大小必须是8字节的整倍数,当实例数据不是8字节的整倍数时,需要填充空间来补上

Java对象头的内容

  • Mark Word

    是对象头的核心内容,记录了对象的锁信息
  • 类型指针

    指向对象类的元数据
  • 数组长度

    如果对象是数组,那还要有数据记录数组的长度

Mark Word的组成

下图为32bit的JVM虚拟机中,Mark Word的组成:

MarkWord通过标志位来记录对象当前的锁信息。不同标示位的情况下,Mark Word记录的数据不一样。

锁的区别

无锁(01)

无锁状态下,对象不具备排他性,此时所有线程都可以直接访问这个对象。

偏向锁(01)

偏向锁实际上就是无锁,此时标记字记录的是当前操作这个对象的线程ID。

轻量级锁(00)

此时对象被锁定,标记字指向持有当前对象的线程的地址。等待的线程会进入自旋状态,通过CAS来争抢锁。

重量级锁(10)

此时对象被锁定,标记字指向当前对象的Monitor(对象监视器)的地址。等待的线程会进入阻塞状态。

锁的升级过程

Java的锁只会升级,不会降级。当锁全部被释放后,会回到初始状态,等待再次被升级。

初始状态

JDK6开始,JVM默认打开了偏向锁,因此默认情况下,一个对象被创建时,对象头中是偏向锁的信息。

  • 打开偏向锁(默认)

  • 关闭偏向锁

有一个线程使用synchronized锁定了这个对象

此时JVM默认永远只有这个线程使用这个对象,为了减少性能消耗,会进入偏向锁状态,实际上并不会上锁。

另一个线程尝试通过synchronized获取对象的锁

当出现了锁争夺时,会升级为轻量级锁,此时两个线程会尝试修改标记字自己的线程地址,修改成功的线程获取到锁,修改失败的线程进入到自旋状态,通过CAS操作来重试修改标记字。

第二个线程自旋到了一定次数,或者出现了更多线程来争夺锁

此时JVM会把对象的锁升级为重量级锁,标记字会指向对象的对象监视器(Monitor),所有争夺失败的线程进入阻塞状态。

重量级锁的内部实现

什么是对象监视器

每个对象都有一个对应的对象监视器,用来控制对象的多线程访问。

对象监视器的内部结构

  • Owner

    指向正持有对象锁的线程
  • EntryList(锁池)

    存放竞争失败的线程
  • WaitSet(等待池)

    存放当前处于等待状态的线程

对象监视器的流程

初始

多个线程竞争对象的锁,争夺成功的线程引用放入到Owner中,其他线程放入到锁池中

线程正常执行

执行完成后,调用monitorExit,释放锁。

此时从锁池中按照先进先出原则,取出下一个线程,和新进的线程(如果有的话)来争夺锁。

调用了Object.wait()方法

调用wait()方法,线程进入挂起状态,线程的引用会被放入到waitSet中,从锁池中按照先进先出原则,取出下一个线程,和新进的线程(如果有的话)来争夺锁。

调用了Object.notify()方法

调用notify()方法后,从waitSet中拿到一个线程(具体拿哪个取决于JVM的配置),立即去争夺对象的锁,如果失败则进入到锁池。

调用了Object.notifyAll()方法

调用notify()方法后,从waitSet中拿出所有线程,立即去争夺对象的锁,如果失败则进入到锁池。

除非明确知道只有一个线程出于wait状态,否则就是用notifyAll方法,防止线程等待太久或永远等下去。

最新文章

  1. SQL Server封闭掉 触发器递归
  2. ajax传输中文乱码解决方法
  3. LCS问题
  4. 配置Tomcat使用https协议
  5. Linux下实现获取远程机器文件
  6. openstack cinder-volume 的高可用(HA)
  7. jQuery基础之(四)jQuery创建DOM元素
  8. uart与usart
  9. 【Mood-3】心声
  10. C语言学习_恶搞小程序
  11. MinGW32 +QT4.8.6+QT Creator+CMAKE的安装
  12. JavaWeb之Filter过滤器
  13. BZOJ 1176: [Balkan2007]Mokia [CDQ分治]
  14. 基于ubuntu16.04部署IBM开源区块链项目-弹珠资产管理(Marbles)
  15. [Bayes] Understanding Bayes: Updating priors via the likelihood
  16. QT 通过QNetworkReply *获取对应请求的URL地址
  17. HTML第二章总结
  18. mysql查看和修改密码策略
  19. day 4 Socket 和 NIO Netty
  20. Linux VMware安装VMTools工具

热门文章

  1. VGG16迁移学习实现
  2. 『动善时』JMeter基础 — 40、JMeter中ForEach控制器详解
  3. Python爬虫入门:Urllib parse库使用详解(二)
  4. python之读取excel实例演示
  5. python 利用三方的xlrd模块读取excel文件,处理合并单元格
  6. 【逆向&渗透实战】Dump内存中的Dex_我是如何脱壳某公司加固过的Apk并利用其API渗透对方数据库
  7. JVM面试题(史上最强、持续更新、吐血推荐)
  8. 你应该这样去开发接口:Java多线程并行计算
  9. [Linux]经典面试题 - 系统管理 - 备份策略
  10. 【学习】自定义view