首页 > 其他分享 >JUC视频学习

JUC视频学习

时间:2024-12-19 13:59:50浏览次数:11  
标签:JUC 加锁 synchronized 视频 学习 线程 轻量级 cpu 偏向

第一节 锁相关知识

线程的并发问题的由来?

比如这里的要执行i++操作;

我们一般时在主存存储i的值,然后再cpu进行运算的;

所以这里的步骤分为三部:1.cpu从内存获取i的值;2.cpu执行运算+1操作;3.cpu将计算结果进行返回;

假设我们这里有两台cpu,一个cpu1一个cpu2;如果cpu1和cpu2同时获取到了i=0;

然后执行加一操作两个cpu返回的都是1;按理来说应该两次加一操作主存中应该是2的,这种情况就是并发问题;

并发问题的解决方案:

1.悲观锁:总的来说就是先加锁然后再操作数据;

通过使用synchronized或者reentrantlock加锁,阻止另一个cpu获取资源进行运算操作;

适用于并发量高的场景,因为乐观锁再并发情况下会出现错误;

2.乐观锁:总的来说就是先操作数据然后再加锁;

一般通过CAS来实现,即比较并交换,会先从内存中取出值,和当前线程的值进行比较,若相等,则可执行,反之,不可执行;

优点:可以允许同时修改;

缺点:同时修改可能会失败;(例如cpu1修改完了0-1了,但是cpu2获取到的主存值为1了,还是想要从0-1就不允许了);

乐观锁是用于并发量低的场景,因为并发高cas一直失败自旋cpu空转消耗资源性能并且性能没有任何意义,不如让cpu执行其他任务;

锁的概念

第一种分类

无锁:就是字面意思,未加锁的情况;

偏向锁:适用于只有一个线程的场景;可能本身并没有并发,程序员水平低 一直加锁,jvm主动做的一种优化;

偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。

如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。

轻量级锁:竞争没有那么激烈的场景,多个线程只需要轻微的等待或者自旋就可以获取到锁;

重量级锁:竞争激烈的场景,会造成线程阻塞或者睡眠;偏向锁经过大概10次竞争就会升级为重量级锁;

第二种分类:

公平锁:线程按照顺序进行排队的锁;

非公平锁:有些线程不按照顺序执行,直接去cpu获取锁资源;

第三种分类:

可重入锁:可重入锁就是一证通/一卡通,只需一张卡就可以通过所有相同关卡。

可重入锁又名递归锁,指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。与可重入锁相反,不可重入锁不可递归调用,递归调用就发生死锁。

不可重入锁(不可重入锁也叫自旋锁):即使每个关卡相同,你也得再拿一个一摸一样的卡来。

第四种分类:

单机锁:一般是单机jvm下,多线程竞争持有锁的场景,保证一个jvm里面只有一个线程能够持有锁;

分布式锁:分布式情况下,多台机器,多线程持有锁的场景,保证的是多个jvm机器里面只有一个线程能够持有锁;

第二节 多线程相关知识:

线程安全需要注意的问题?

原子性:为了保证当前线程的操作不会收其他线程的影响;保证执行任务全部成功或者全部失败;

有序性:代码按照顺序执行,但是cpu也会有一定的优化原则可能会调整执行顺序;

可见性:就是说一个共享变量再被多个线程操作时,是否保证结果正确;例如两个cpu,和一个主存对变量i进行加一操作;然后第一个cpu获取到i=0之后进行加一,此时第二个cpu也获取到了i=0进行加一操作的情况;

volatile可以解决什么?

volatile是一个轻量级的synchronized,一般作用与变量,在多处理器开发的过程中保证了内存的可见性。相比于synchronized关键字,volatile关键字的执行成本更低,效率更高。

可以解决有序性:

volatile变量的修饰使得JVM在生成字节码时会在该变量的读写前后插入内存屏障,从而避免了指令的乱序执行问题。

可以解决可见性:

当一个线程修改了volatile变量的值时,其他线程可以立即看到这个变化,因为volatile变量的读写操作会加入到内存屏障,强制刷新缓存和主内存的数据一致性。也是通过MESI协议;

volatile存在什么问题?

无法解决原子性

大量的volatile会导致总线风暴(超纲知识点)

synchronized原理

基础知识

什么是上下文切换?

cpu切换执行的线程就是上下文切换。实际上就是线程的阻塞和唤醒;

上下文切换有什么问题?

会引起资源的消耗。因为cpu需要存储一些临时的计算变量等;

synchronized的缺点?

性能较差,效率低;

jdk1.6之后的优化操作:

锁升级:

https://zhuanlan.zhihu.com/p/290991898

相关知识:?

考虑的是堆中的对象的数据结构:对象头、实例数据和对齐填充子节;

对象头又分为:mark word、指向对象的指针和数组长度(只有数组对象有)

偏向锁:偏向锁是Java 6之后加入的新锁,它是一种针对加锁操作的优化手段。偏向锁是最乐观的一种情况:在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得。 因此为了减少同一线程获取锁的代价而引入偏向锁。

偏向锁的核心思想是:如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,直接可以获取锁。这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。

加锁时,如果该锁对象支持偏向锁,那么 Java 虚拟机会通过CAS操作,将当前线程的地址(我理解的是线程ID,不过都能确定唯一线程)记录在锁对象的标记字段之中,并且将标记字段的最后三位设置为101

轻量锁:轻量级锁是一种比较乐观的情况:多个线程在不同的时间段请求同一把锁,也就是说没有锁竞争。
标记字段(mark word)的最后两位被用来表示该对象的锁状态。其中,00 代表轻量级锁,01 代表无锁(或偏向锁),10 代表重量级锁。

当进行加锁操作时,Java 虚拟机会判断是否已经是重量级锁。如果不是,它会在当前线程的当前栈桢中划出一块空间,作为该锁的锁记录,并且将锁对象的标记字段 复制到该锁记录中(可以理解为保存之前锁对象的标记字段。如果是同一个线程这个值会是0:后面的锁记录清零就是这个意思)。

为什么要有锁升级?

主要是提高程序的执行效率,mutex互斥变量没有拿到锁的线程会阻塞睡眠,引起cpu上下文切换;

官方一点:为了尽量避免昂贵的线程阻塞、唤醒操作,Java 虚拟机会在线程进入阻塞状态之前,以及被唤醒后竞争不到锁的情况下,进入自旋状态,在处理器上空跑并且轮询锁是否被释放。如果此时锁恰好被释放了,那么当前线程便无须进入阻塞状态,而是直接获得这把锁。我们称其为自旋锁

Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁偏向锁

执行流程:

首先是无锁,

然后初次执行到synchronized代码块的时候,锁对象变成偏向锁;

然后轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。

然后如果锁竞争情况严重,某个达到最大自旋次数的线程,会将轻量级锁升级为重量级锁(依然是CAS修改锁标志位,但不修改持有锁的线程ID)。当后续线程尝试获取锁时,发现被占用的锁是重量级锁,则直接将自己挂起(而不是忙等),等待将来被唤醒。

锁粗化:实际上就是因为多个synchronized代码块相连的情况,不如直接合并为一个synchronized代码块;

锁消除:使用synchronized的效率低,直接消除这个synchronized代码块;

ThreadLocal

什么是threadlocal?

是一个线程变量,就是threadlocal中填充的变量是属于当前线程的,该变量对于其他线程而言是隔离的;

为什么要有threadlocal?

threadlocal适用于每个线程需要有自己的实例,并且该实例要在多个方法中被使用;

例子:假设我们在拦截器获取到了用户信息,然后再请求链路上都可能需要使用用户信息,那么这个用户信息放在哪里?

1.每次方法调用时将用户信息作为参数;

2.设置为public static 静态变量;

结论:1和2都不合适;1太麻烦,需要频繁传递参数;方案2单线程没有问题,多线程就会导致多个线程公用一个用户信息,引起线程安全问题;

设计思路?

每个线程都有一个自己的threadlocalmap,其中的key是threadlocal实例,value是我们set进去的值;

引用设计与分析?

为什么key是弱引用,为了避免方法结束之后这里的threadlocal还没有被回收;

为什么value不是弱引用?

threadlocal面试题?

ThreadLocal与Synchronized的区别?

1.synchronized用于线程之间的数据共享,而Threadlocal则用于线程之间的数据隔离;

2.synchronized是利用的锁机制,使得变量或者代码块再某一时刻只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程再某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而synchronized刚好相反,它用于再多个线程间通信时能够获得数据共享;

标签:JUC,加锁,synchronized,视频,学习,线程,轻量级,cpu,偏向
From: https://blog.csdn.net/qq_58738794/article/details/144507126

相关文章