多线程学习笔记(三)
创始人
2024-03-25 03:21:34
0

一、缓存行

缓存行:从缓存中读取数据是按照一块来读取的,这一块叫做缓存行,64字节大小
缓存一致性协议:当两个数位于同一个缓存行时,有两个线程需要同时读取了缓存行中的数据后进行修改,需要和另一个线程的数据保存一致

可以通过在数据前后补充空数据来保证两个数据不在同一个缓存行,来避免反复进行缓存一致性(jdk1.7中LinkedBlockingQueue就用了此方式)

jdk1.8中,可以通过@Contended来保证数据自己在一个缓存行中,          需要在jvm运行参数中加  -XX:RestrictContended

缓存一致性协议根据不同cpu有不同实现,intel的cpu的缓存一致性协议是MESI

M:Modified

E:Exclusive

S: Shared

I: Invalid 

二、乱序

1. 为什么会出现乱序

为了提高效率

2. 乱序的存在条件

不影响单线程的最终一致性(as if serial)

指令重排可能会导致对象逃逸(不要在构造方法中启动线程)

3. 对象在内存中的存储布局

 对象的大小是8字节的倍数,如果前面三个占用10字节,最后的padding需要加6字节对齐

markword占8字节

class pointer默认压缩,占用4字节

instance data默认压缩

对象头包含markword和class pointer

4. hanppens-before 原则

JVM规定重排序必须要遵循的规则

5. 内存屏障

使用内存屏障阻止乱序执行

内存屏障是特殊指令:看到这种指令,前面的必须执行完,后面的才能执行

intel的cpu的屏障指令:lfence(load 读)、sfence(store 写)、mfence(读写)

6. jvm中的内存屏障

LoadLoadBarrier、LoadStoreBarrier、StoreLoadBarrier、StoreStoreBarrier

7. volatile怎么实现禁止指令重排序

在volatile写操作前面加StoreStoreBarrier,后面加StoreLoadBarrier

在volatile读操作前面加LoadLoadBarrier、后面加LoadStoreBarrier

volatile在hotspot中的实现是通过 lock addl指令

三、原子性

1. 概念

不能被打断的操作,需要作为一个整体执行的操作,叫做原子操作

2. 上锁的本质

把并发编程序列化

3. 实现

通过乐观锁和悲观锁来保证线程的原子性

乐观锁:认为这个操作不会被别的线程打断,使用cas操作

悲观锁:认为这个操作会被别的线程打断,synchronized就是悲观锁

4. CAS:compare and swap

假设要给a + 1,先读取到a,然后把a值+1,再去跟一开始读取的a进行比较,如果此时读取到的a跟原来的一样,就给a替换成新值,否则,重新读取a再进行操作

ABA问题,如果比较的a跟之前一样,但是中间可能已经发生了改变,只是又变回原来的值了

解决ABA问题,加version

CAS操作本身要保证原子性,要不然中间可能会出问题

AtomicInteger可以实现原子性,通过CAS操作,在汇编指令上通过lock cmpxchg实现

5. 选择乐观锁还是悲观锁

临界区执行时间较长,等待线程较多,使用悲观锁

临界区执行时间短,等待线程较少,使用乐观锁

四、synchronized

用户态:只能执行部分指令

内核态:所有指令都可以执行

jvm工作在用户态,synchronized重量级锁要通过操作系统才能申请到 

1. markword

 根据最后两位判断,

如果是00,表示轻量级锁

如果是10,表示重量级锁

如果是01,需要根据倒数第三位来判断,如果倒数第三位是0,表示无锁(刚刚new出来);如果是1,表示偏向锁

偏向锁和轻量级锁是用户空间的完成

重量级锁需要向内核申请

2. 重入次数

synchronized是可重入锁,需要记录重入次数

偏向锁和轻量级锁的重入次数记录在线程栈中,LockRecord+1

重量级锁记录在ObjectMonitor上

3. 锁升级

 

jvm刚启动时,默认不开启偏向锁,默认4秒后开启偏向锁(可以通过参数设置)。

刚启动就开启偏向锁的对象没有偏向,所以就称为匿名偏向。

匿名偏向对象升级的时候升级为偏向锁,偏向锁升级的时候升级为轻量级锁

没有偏向的对象会直接升级为轻量级锁

当第一个来申请锁的对象会把自己的线程id放入markword中,称为偏向锁

当好几个对象都来申请锁的时候,会通过CAS自旋(在线程栈中写入LockRecord)来升级为轻量级锁

jdk1.4中通过自旋次数,占用cpu数来决定升级为重量级锁,1.6及以后,自适应升级重量级锁

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...