Java多线程笔记/java和go多线程区别/锁升级/锁的底层实现/volatile底层实现

本篇为根据马士兵老师公开课所讲内容记录的笔记。十分感谢马老师精彩讲解。
视频地址: 马士兵老师公开课

用户态、内核态

以前的操作系统,os(操作系统)和app(用户应用程序)是在同一级别。这就导致app可以修改操作系统所占用的内存等,导致系统崩溃。

image-20200707143333897

现在的操作系统,内核与硬件(内存、网卡等)打交道,所有的app运行在内核的外部。app需要与内核交互才能获取相应的资源。

image-20200707143510729

(蓝色矩形代表内存)

java多线程模型

  • JVM虚拟机规范没有要求。Hostpot(Oracle的JVM实现) 1:1。
    • JVM每个线程对应OS中一个线程。
    • 线程锁、线程调度都交给OS来处理
    • 重量级线程,OS管理线程
  • go 语言 纤程M:N
    • 轻量级线程,不需要OS管理线程。自己管理
    • M:N 具体实现是GPM模型。M个分成不同的队列,OS里有N个实际线程。N个实际线程去队列里不断的取任务

进程与线程区别

进程:分配资源的基本单位。给进程分配内存空间、端口、文件句柄等。

线程:执行操作的基本单位。进程运行起来是从main线程开始,实际上是不同的线程。他们占用的都是进程分配的那些资源。

线程不是越多越好,因为线程切换时cpu要进行保存线程和恢复,切换过多反而会浪费时间。

线程的数量往往有压力测试来决定。

Syncnaized从重量级锁升级到轻量级锁(JDK1.2)

AtomicInteger 原子类实现:

最终调用compareAndSwapInt()实现,调用的C++

底层:CAS

compare and swap/compare and exchange

在往回写的时候,如果原数没变,说明没人改,就写回去。

如果有人改,就重新读出来重新加。直到成功

这就是自旋锁/乐观锁

问题:

  • A->B->A问题:例如,A线程改之前m=1,中间m被其他线程修改成2,然后又改成了1,这时A线程在回写的时候,并不能发现m被人改过。解决方案:给m加版本号。
  • compare and set过程是否具备原子性? 初始m=1
    • 如果线程A顺利回写m=2,则两步:1.m没被改变,仍然为1, 2. 给m新值。
    • 如果在1,2步之间,另一个线程读取了m,并写回了新值m=8, 那么线程A回写会将m=8覆盖掉。
    • 所以比较和赋值必须是原子性,不能被打断。
    • 具体实现在c++,利用了CPU的指令,用汇编嵌入写的。cmpxchgl,底层支持。

简单同步,优先使用synchrasied,因为现在已经升级了CAS。

面试重点

image-20200707160054502

  • markword 64位jvm,8字节。
  • 指向具体.class地址,getClass()就是根据这个指针拿的。
  • 类包含的实例数据
  • padding 对其为了让他能被8字节整除。装配效率高。

org.openjdk-jol 能够打印类的内存布局信息。

System.out.println(ClassLayout.parseInstance(o).toPrintable());

一个new Object在内存中占用16个字节。

markword信息:

  • 锁信息
  • hashcode
  • gc信息

java内部实现一般小端

锁升级过程

偏向锁,严格讲不是一把锁,只是在markword里记录了线程指针。

自旋锁,竞争锁。谁抢到是谁的,没有等待队列,消耗cpu。通过CAS操作。谁填进去自己的ID号就是谁的。不需要OS调度。

重量级,有等待序列,需要CPU调度。但是不消耗cpu资源。调用wait或者notify的一定是重量锁。

重量所一定比自旋锁效率高吗?

不一定。自旋锁如果有个在里面时间很长,其他一直在外面争抢。

1.6之后自动适应,什么时候升级成重量级JVM控制。

image-20200707163642930

偏向锁有一个使用时延。新建一个普通对象时,上来是自旋锁,然后4s之后如果再变成偏向锁。

Volatile

  • cpu的缓存行,一般是64字节。每次都换一行。为了追求极致性能,可以在两个变量中间加入其他的无用数据,强制让这两个变量不在一个缓存行,那么在多线程同时修改这两个数据时,volatile同步性能会有较大的提升。因为不用频繁通知更新这个变量了。(要用static修饰。)

指令重排序

指令重排序:cpu运行的顺序可能与自己写的代码顺序不一样,这种现象叫做乱序执行。当编译器认为两行没有关系的时候,可能会改变顺序来提升效率。

可以发生指令重排序的:JVM 规定8种可以。 搜索关键词: happens before

volatile能够禁止指令重排序的原因:

在两个指令之间添加一个屏障: memory barriar

JVM级别的内存屏障:四种

image-20200708154044277

Volatile实现细节:

  • JVM规范的要求:

    在对volatile声明的内存写前后都加入屏障,读之后加入屏障

image-20200708154259472

  • 底层硬件的实现:

    hostpot实现,在x86平台,使用的是cpu的lock指令,

    该指令对共享的内存单独使用。能够将当前cpu用的缓存写到内存,令其他的cpu缓存失效。还提供了有序指令无法越过内存屏障的作用(锁总线了)

线程池面试题

多线程发起多个任务,如果所有校验结果为true,就返回true,只要有一个返回false,就返回false。尽量快。请完成isAllTrue()

分析: 只要有一个返回false了,就直接返回结果。不能等待全结束再返回,除非全是True。取消掉其他线程。

Future-> Future.get()是阻塞的。

guava -> ListenableFuture,不阻塞,直接返回。JDK1.8出了个CompletableFuture。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值