多线程面试问点

1.什么是CAS
CAS是高并发中的一个关键,解决高并发场景下的线程问题的,CAS是一个比较替换的过程,当线程进来时,例如需要进行对某个值进行更新操作,在传统的情况下是需要进行上锁的操作,确保原子性的,在jdk5之后有一些原子性的变量可以替代,而CAS是一个进行比较替换的过程,当线程获取到数据后,会new出来一个新的对象进行接收,如果第二获取时,数据依旧还是第一次获取的数据时,这个个时候就会将new出来的新值赋值给这个数据,如果要是不是第一个获取到的数据的话,这个时候就或重新进行获取数据值,在重新对比比较赋值,这里也就是CAS比较替换。
多线程面试问点_第1张图片

2.ABA问题
ABA问题就是在多线程的情况下, 数据给A线程获取时数据是0,而当B线程进来的时候,将数据从0改为了5在改回0,经历了一次替换,而A线程进行CAS操作的时候,发现依旧是0,但是实际是并不是原来的那个0,这也就是经典的ABA问题。
如何解决ABA问题呢,是非常简单的,可以对操作的数据添加版本号,通过比较版本和值,这样就能解决ABA 问题了。
这段就是CAS的典型应用,自增操作

3.CAS的实现
CAS的实现与synchrnoized是一样的都是通过调用cmpxchg指令来实现的,然而cmpxchg指令并不是原子性的,也就是说,他也无法保证在多线程的情况下,数据的原子性,而cmpxchg指令是c++中的指令,是针对处理器来的,在一个处理器的情况下,是可以保证,但,现在大多数的是多核处理器也无法实现了,这个时候,在引用C++的时候添加了lock_if_mp指令,通过添加lock进行确保cmpxchg的原子性操作,CAS是无锁的,也可以称为轻量级锁,在对象创建后使用synchrnoized升级第一个阶段就是这个无锁(轻量级锁阶段)
3.对象布局
多线程面试问点_第2张图片
为啥要提对象布局呢,因为synchrnoized等锁与java对象在内存中的布局是息息相关的,有直接的影响
在使用了synchrnoized和没有使用synchrnoized的对象布局是不同的,而synchrnoized锁有说是对代码进行上锁,其实这是不对滴,他是锁住门,在入口上锁,而不是对过程或者是代码片段,并且synchrnoized锁的信息是在对象的MarkWord里面,MarkWord里面存放的是对象信息的2个对象头信息,一共八个字节,而对象头里面包含了MarkWord 和class porinter两个部分
对于实例数据是进行变更改变的,如果new的是一个long,那么实例数据里面占据的是8个字节,要是new的是一个int类型的数据,那么他占据的是4个字节,成员变量的数据也就是在这个实例数据里面,对于对齐来说,他是为了帮助数据的整体字节数如果不能被8整除的时候,他会进行补齐操作,将字节数变为可被8整除的数据,为啥是要给8整除呢,那是因为在线程总线中,被8整除会将运行效率提高,是为了改善效率使用的这个对齐功能
在内存中,MarkWord占据8个字节,class prointer占据4个字节,实例数据与对应的实例变量进行相对于应,而对其padding的字节数据是为其markWord class prointer 和 实例数据instance data 进行补位成可以被8 整除的数据字节数
4.synchrnoized锁
synchrnoized锁初始化的时候是偏量锁,然后升级为无锁也就是和CAS一样,也叫轻量级锁,自旋锁,当这些都不能解决问题的时候就会继续升级为重量级锁
多线程面试问点_第3张图片
这些升级转态的信息全部记录在对象信息的MarkWord信息中,markWord中不仅仅包含了锁的信息,还有GC的信息也存放在这个里面
4.1 偏量锁
偏量锁是第一种锁的状态,这个是针对于没有过多竞争者的时候(可能会有竞争)使用的锁,如果线程一上来就使用重量级锁的话,每次使用锁都需要想操作系统进行申请,资源消耗很大,而轻量级锁的话偏向于第一次进来的,也就是说,他不需要向操作系统进行申请,只是进行声明,在资源上贴上“标签”,并且线程处理效率很快,竞争不是特别大,当前线程很快就能处理完成,下个线程很快就能获取到原子性数据的操作,这里的“标签”是通过java thread 里面的54位的指针,记录当前线程的操作者
4.2 轻量锁
如果竞争存在,并且竞争量不是特别大,那么锁的状态就会自动升级,偏量锁这个时候就不会用54位的指针指向线程了,而是升级为轻量级锁,通过64位的指针,指向某一个线程中的线程栈中的lock record的指针,也就是通过cas的模式进行竞争,一旦可以进行数据更改的时候,就会贴上对应线程的lock record指针,进行上锁
4.3重量锁
如果竞争量特别大,并且每一个线程处理的时间较长,这个时候的话就会将轻量锁自动升级为重量级锁(轻量级锁自旋超过10,或者自旋超过cup核数的1/2,在jdk1.6之后引入了自适应自旋的模式,可以通过jvm进行设置),在升级成为重量级锁的时候,需要通过用户态向内核态去申请一个互斥量(重量级锁)的指针,而且,这个时候线程就会贴上这个互斥量指针了
重量级锁的好处
有了重量级锁的引入,这个使用在轻量级长时间进行自旋的数据就放到重量级的队列里面(设置重量级锁后是存在队列的),这个时候就不会使用cup进行自旋了,直到上个线程使用完成后,从队列中逐个进行使用资源,可以减轻cup的负担。
重量级锁的缺点
缺点就是,每次设置锁的时候需要从用户态向内核态中申请锁,这个申请是性能特别低的,也就是说,能使用轻量级锁的时候就用轻量级,而面对竞争特别大,处理时间不是特别迅速的时候,可以使用重量级锁
5.synchronize的实现结构
多线程面试问点_第4张图片
6.volatile
volatile的作用是为了保证线程可见性
比如,在一个while循环中,一个线程的while的条件进行了更改,这个时候,while条件是不会进行停止的,需要通过添加volatile关键字在while条件的上,这样就能及时的将最新的数据给原线程推送过去
多线程面试问点_第5张图片
7.超线程
如果有一个任务是需要进行运行从1到100000的数据和,但是为了效率,采用了多个线程进行协调,这个时候呢如果A线程进行操作,将1-5000 进行运算,而B线程从5001-10000,而在多线程切换的时候,需要将这些数据进行缓存,存到一个寄存器中,然后获取到B线程,开始继续计算,计算完成后,继续存放到寄存器中,A线程如果需要继续执行的话,就需要在寄存器中获取到上次执行的位置,再次进行计算,这也就是多线程之间的上下文切换,是非常浪费资源的,而超线程呢,是指的是他有多个寄存器,在进行切换运算的时候,不需要切换所有的数据,只需要将逻辑计算器的指向进行改变,就能提高效率,进行再次运算,也就是所谓的4核8线程
多线程面试问点_第6张图片
多线程面试问点_第7张图片
7.cache line
cache line的概念是缓存行对齐伪共享,一个cache line数据是8个字节,
线程进读取缓存的时候是按照一个缓存块进行读取的,一个缓存行是64位,如果读取的一个缓存行里的数据是相邻的话,每次修改一个数据的时候,都需要通知另个线程,效率的话就会降低,如果不是在同一个缓存行里面的话,每次修改的时候就不需进行通知,相对来说,性能会提高

更多内容,https://www.bilibili.com/video/BV1xK4y1C7aT?p=5&spm_id_from=pageDriver

你可能感兴趣的:(java)