Java锁机制是什么?(上)

什么是锁?

在并发环境下,多个线程会对同一个资源进行争抢,可能会导致数据不一致的问题。为了解决这一问题,需要通过一种抽象的锁来对资源进行锁定。首先,我们需要了解JVM运行是的内存结构:
在这里插入图片描述
图中表明红色区域是各个线程所私有的,不会出现资源竞争的问题,而蓝色区域中的数据被所有线程共享。其中Java堆中存放的是所有的对象,方法区中存放着类信息、常量、静态变量等数据。

所以在多个线程在对资源进行竞争的过程中难免会出现异常情况,因此需要锁机制对其进行限制。

锁机制是如何实现的?

在Java中,每个对象都拥有一把锁,这把锁存放在对象头中,锁中记录了当前对象被哪个线程所占用。

  1. 对象的结构
    Java对象包含三个部分,对象头、实例数据和对齐填充字节。
对象头实例数据对齐填充字节
对象运行信息:Mark Word + Class Point属性、方法使对象大小 = 8bit * n

其中对齐填充字节是为了满足 Java对象的大小必须是8比特的倍数 这一个条件设计的。
实例
实例数据是初始化对象时设定的属性和状态的内容。

  1. 对象头的结构
    对象头存放了对象本身的运行时信息,包括Mark Word 和 Class Point。Class Point相当于一个指针指向了当前数据类型所在方法区中的类型数据。Mark Word存储了很多和当前对象运行时状态信息有关的数据,如:hashcode、锁状态标志、指向所记录的指针等等。
    在这里插入图片描述
    Mark Word只有32bit,并且Mark Word是非结构化的,这样再不同的锁标志位下,不同的字段可以重用不同的比特位,因此可以节省空间。

synchronized的同步机制

在Java中synchronized可以用来同步线程,在synchronized被编译后会生成monitorenter和monitorexit两个字节码指令,并依赖这两个字节码指令来进行线程同步。monitor是一个只能容纳一个线程的空间。打个比方:monitor可以想象成一个只能容纳一名客人的房间,把想要获取对象的线程想象成进入这个房间的客人,一个线程进入了monitor,其他的线程就只能够进行等待,只有当这个线程退出的时候,其他的线程才有进入的机会。
在这里插入图片描述
上图表明,首先entryset中聚集了一些想要进入monitor的线程,他们正处于waiting的状态。当一个线程A进入monitor的时候,它就处于active状态,离开时就是waiting状态。新进入的进程B可以通过notify的形式唤醒wait set中的线程A,让线程A再次进入monitor中执行任务,执行完后方可退出。

因为monitor是依赖于操作系统的mutex lock来实现的,Java线程实际上是对操作系统线程的映射,所以每当挂起或者唤醒一个线程,都要切换操作系统的内核态,这些操作比较重量级。在一些情况下,线程切换的时间甚至会超过线程执行的时间。这时,使用synchronized会对程序的运行产生非常严重的影响。

在Java 6之后,synchronized进行了优化,引入了偏向锁、轻量级锁。所以锁总共有四种状态,从低到高分别是无锁、偏向锁、轻量级锁和重量级锁。这四个锁状态分别对应了Mark Word中的四个状态。注意:锁只能升级不能降级。

锁的四种状态

  1. 无锁
    无锁,即没有对资源进行锁定,所有线程都能够访问到同一资源。使用情况:1.无竞争 2.存在竞争,使用非锁方式同步线程。CAS(Compare And Swap)在操作系统中通过一条指令实现,所以它就能够保证原子性。可以通过CAS的方式实现无锁编程。

  2. 偏向锁
    偏向锁,即不通过mutex lock线程切换,不通过CAS来获得锁,对象和线程之间具有匹配功能,只要是这个线程过来,对象直接把锁交出去。通过Mark Word中的线程ID来找到占用这个锁的线程。

  3. 轻量级锁
    当发现有多个线程在竞争锁的时候,偏向锁将会升级为轻量级锁。

当一个线程想要获得某个对象的锁时,假如现在是轻量级锁,这时线程会在自己的虚拟机栈中开辟一块被称为Lock Record的空间,Lock Record中存放的是对象头中Mark Word的副本以及owner指针。线程通过CAS去尝试获取锁,一旦获得那么就会复制该对象头中的Mark Word到Lock Record,并且将Lock Record中的owner指针指向该对象。

另一方面,对象的Mark Word的前30个bit将会生成一个指针,指向线程虚拟机栈中的Lock Record。这样就实现了线程和对象锁的绑定。

其他想要获取这个对象锁的线程将会自旋等待,自旋类似于一种轮询,线程自己在不断地循环尝试去看锁有没有被释放,如果释放了就会去获取,否则就进入下一轮循环。

自旋相当于CPU在空转,长时间自旋会浪费CPU的资源,于是出现了一种 适应性自旋 的优化,就是自旋的时间不在固定,是由上一次在同一个锁上的自旋时间和锁状态来决定的。

一旦自旋等待的线程数超过一个那么 轻量级锁就会升级为重量级锁。

  1. 重量级锁
    此时通过monitor对线程进行控制,此时将会完全锁定资源,对线程的管控最为严格。
    在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值