多线程相关——线程安全(大部分都比较难)

01-线程安全-synchronized关键字的底层原理(非常难!!!)

synchronized锁是对象锁,是一种互斥锁,最多只有一个对象持有它。

底层:Monitor

汇编语言:

第二个解锁:防止代码出现异常而没人去解锁

jvm级别的对象,C++实现

Monitor的数据结构:

1、WaitSet

2、EntryList

3、Owner

检查Owner是否为null,是则直接让线程获取锁,否则进列(Blocked)

02-线程安全-synchronized关键字的底层原理(进阶)

问:

(重量级锁,性能不高)

Jvm-->内核态

进程的上下文切换

1、重量级锁

lock和Monitor是怎么关联上的呢?

先讲讲对象的内存结构

——MarkWord

在lock对象头的Mark Word中,如果后两位为10,表示它与一个Monitor连接。

而前30位是一个指针,指向重量级锁。

2、轻量级锁

线程1调用method1,method1调用method2,线程进入同一把锁两次(锁重入)

因为是同一个线程持有两把锁,所以不存在竞争,使用轻量级锁。

重点还是来看Mark Word:

在一开始没有人调用method1时,Mark Word记录为上面第一行所示的那样。

当调用method1时,在线程中会产生一个Lock Record,其中Object reference指向Object对象,Lock record的地址是用来替换Object中的地址的(CAS操作——保证在修改数据的时候是原子操作)。

退出锁:

首先判断Lock Record是否为null,如果为null(则是重入锁),可以直接删除;

如果不为null,则需要先进行一次CAS操作拿回Mark Word,再进行删除。

一图:

3、偏向锁

CAS操作与轻量级锁不一样:

偏向锁——写入thread id和末尾101即可

重入锁时,不会进行CAS操作,只是做判断——线程id是否一致。

一图:

一旦锁发生了竞争,都会升级为重量级锁

03-线程安全-JMM(Java内存模型)

Java Memory Model(JMM)

定义了共享内存多线程程序读写操作的行为规范

在创建一个线程时,会创建一块工作内存,且其他线程无法访问这一部分,所以不存在线程安全问题。

对于主内存,里面存储了很多共享变量,多个线程都可以访问它们,所以存在线程安全问题。

多个线程需要通过主内存对数据进行同步。

一图流:

03-线程安全-CAS

有很多底层框架使用到了CAS的思想,虽然它出现频率不高,但是思路很重要。

Compare And Swap(CAS),它体现的一种乐观锁的思想,在无锁的情况下保证线程操作共享数据原子性

一图流:

03-线程安全-volatile

volatile是一个关键字,用于修饰共享变量:

1、保证线程间的可见性

对于线程之间对共享数据的操作,它先取了共享数据放到了工作内存中。

如果它是不可见的,那么一个线程对共享数据的修改不会影响另外一个线程。

问题:JIT(即时编译器)对代码进行优化后,线程就对stop的变化“无视”了。

2、禁止指令重排序

问题:

@Actor保证方法内的代码在同一个线程下执行

情况4,出现了先执行第二行代码的情况,说明指令被重排序

Volatile关键字的作用:

一图流:

04-线程安全-AQS

AbstractQueuedSynchronizer AQS 抽象队列同步器

基本工作机制

请求锁是FIFO的

原子性保证——CAS

AQS——公平锁/非公平锁

非公平锁

此时线程5未排队直接和线程1抢锁——非公平锁

公平锁

线程5直接去排队,直接把锁给线程1

一图流:

05-线程安全-ReentrantLock

底层用AQS实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值