java 锁相关

在这里插入图片描述
AtomicInteger对应的方法如下:
在这里插入图片描述
在这里插入图片描述
调用了C++的如下代码
在这里插入图片描述

这里看到有一个LOCK_IF_MP,作用是如果是多处理器,在指令前加上LOCK前缀,因为在单处理器中,是不会存在缓存不一致的问题的,所有线程都在一个CPU上跑,使用同一个缓存区,也就不存在本地内存与主内存不一致的问题,不会造成可见性问题。然而在多核处理器中,共享内存需要从写缓存中刷新到主内存中去,并遵循缓存一致性协议通知其他处理器更新缓存。
Lock在这里的作用:

在cmpxchg执行期间,锁住内存地址[edx],其他处理器不能访问该内存,保证原子性。即使是在32位机器上修改64位的内存也可以保证原子性。
将本处理器上写缓存全部强制写回主存中去,也就是写屏障,保证每个线程的本地内存与主存一致。
禁止cmpxchg与前后任何指令重排序,防止指令重排序。
在这里插入图片描述
在这里插入图片描述

Java对象头:
  在Hotspot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充();Java对象头是实现synchronized的锁对象的基础,一般而言,synchronized使用的锁对象是存储在Java对象头里。它是轻量级锁和偏向锁的关键.

import org.openjdk.jol.info.ClassLayout;

public class test001 {

    public static void main(String[] args) {
        Object demo = new Object();
        System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        synchronized (demo) {
            System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        }
    }
}

可以看到加锁后除了Mawrk Word中不同,其他的都是相同的,后面的001代表的是无锁,00代表轻量级锁,偏向锁未启动,直接升级成轻量级锁
在这里插入图片描述

某个应用程序过分的访问这些硬盘,内存等资源,就会导致整个系统的资源被占用,如果不对这种行为进行限制和区分,就会导致资源访问的冲突。所以,Linux的设计的初衷:给不同的操作给与不同的“权限”。Linux操作系统就将权限等级分为了2个等级,分别就是内核态和用户态
在这里插入图片描述
在这里插入图片描述

锁升级的过程:
级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁

在这里插入图片描述
偏向锁:把自己的线程Id放到对象的Mawrk Word中

在这里插入图片描述
之前从轻量级锁(自旋锁)升级到重量有两个条件:
1.自旋次数>10,2.自旋的线程>cpu的一半
现在是JVM来决定自旋几次升级

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。
简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资

在这里插入图片描述

偏向锁是jvm启动4秒后自动打开,对照上图101代表的是偏向锁

import org.openjdk.jol.info.ClassLayout;

public class test001 {

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        Object demo = new Object();
        System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        synchronized (demo) {
            System.out.println(ClassLayout.parseInstance(demo).toPrintable());
        }
    }
}

在这里插入图片描述
没有打开偏向锁的时候,是一个无锁状态001,但打开后变成了偏向锁101,可以通过下图来解释这个现象。
在这里插入图片描述

synchronized代码块

synchronized代码块在jvm中是monitorenter和monitorexit指令实现,两个monitorexit是因为一个是正常结束,一个异常结束的位置
在这里插入图片描述
当执行monitorenter指令时,当前线程将试图获取 objectref(即对象锁) 所对应的 monitor 的持有权,当 objectref 的 monitor 的进入计数器为 0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。如果当前线程已经拥有 objectref 的 monitor 的持有权,那它可以重入这个 monitor (关于重入性稍后会分析),重入时计数器的值也会加 1。倘若其他线程已经拥有 objectref 的 monitor 的所有权,那当前线程将被阻塞,直到正在执行线程执行完毕,即monitorexit指令被执行,执行线程将释放 monitor(锁)并设置计数器值为0 ,其他线程将有机会持有 monitor 。

synchronized方法

使用javap -v查看jvm信息,synchronized方法时用的

方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor, 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛 出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放

在这里插入图片描述

参考::
https://blog.csdn.net/javazejian/article/details/72828483

硬件层数据一致性

多线程底层:CPU去访问内存的数据时,会锁住bus,避免多个CPU取数据发生数据不一致的情况
在这里插入图片描述
一致性协议 MESI:
为了解决总线锁效率低下的问题,Inter用了MESI协议来处理
https://www.cnblogs.com/z00377750/p/9180644.html
在这里插入图片描述
现代CPU解决数据一致性:缓存锁+总线锁

缓存行:
x和y处于一个缓存行(缓存最小单位,64节的数组)
CPU1只读x,但是会读取整个缓存行x和y,当修改了x后,需要通知其他的读取了该行的CPU2
CPU2只读y,但是会读取整个缓存行x和y,当修改了y后,需要通知其他的读取了该行的CPU1
CPU1只关心x,CPU2只关心y,但现在他们相互影响
使用缓存行的对齐来提高效率
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值