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实现