廖雪峰的网站怎么做的,运城网站制作公司,wordpress统计在线人数,自己建设的网站打开慢前言 #x1f31f;#x1f31f;本期讲解关于锁的相关知识了解#xff0c;这里涉及到高频面试题哦~~~ #x1f308;上期博客在这里#xff1a;【JavaEE初阶】深入理解线程池的概念以及Java标准库提供的方法参数分析-CSDN博客 #x1f308;感兴趣的小伙伴看一看小编主页本期讲解关于锁的相关知识了解这里涉及到高频面试题哦~~~ 上期博客在这里【JavaEE初阶】深入理解线程池的概念以及Java标准库提供的方法参数分析-CSDN博客 感兴趣的小伙伴看一看小编主页GGBondlctrl-CSDN博客 目录
️1.引言
️2.锁的策略
2.1乐观锁与悲观锁
2.2轻量级锁和重量级锁
2.3自旋锁和挂起等待锁
2.4普通互斥锁和读写锁
2.5公平锁和非公平锁
2.6可重入锁和不可重入锁
️3.synchronized的加锁过程
3.1锁升级
1.偏向锁阶段
2.轻量级锁
3.重量级锁
3.2锁消除
3.3锁粗化
️4.CAS的实现原理
4.1CAS的内部逻辑
4.2CAS实现线程安全
4.3CAS的原子性
️5.总结 ️1.引言 Hellouu们小编又来啦上期在介绍过线程池的理解后相信大家已经对其有了更深的了解致此多线程初阶已经完结前面的博客也可以供大家学习复习哟~~~
本期只要是讲解关于不同锁的不同的意义理解例如乐观锁和悲观锁以及synchronized的加锁之前的操作.......那就直接开始吧
️2.锁的策略
2.1乐观锁与悲观锁
这是锁的两种不同实现方式 乐观锁即加锁之前预估在程序中的锁的冲突不大因此在加锁的时候就不会进行太多的操作 悲观锁 即加锁之前预估在程序中的锁的冲突很大因此在加锁的时候就不会进行比较多的操作 乐观锁的影响 由于加锁的工作很少所以加锁的时候就很快但是缺点就是会造成更多的CPU资源的消耗引入一些其他问题
悲观锁的影响由于加锁的时候工作比较多所以在加锁的时候就比较满所以此时造成的问题就很少
2.2轻量级锁和重量级锁 轻量级锁消耗的CPU资源多加锁比较快这里理解为乐观锁 重量级锁消耗的CPU资源少加锁比较慢这里理解为悲观锁 这里的轻量级锁和重量级锁是加锁后对锁的一种评价而乐观锁和悲观锁是加锁前的一种预估这里是从两种不同的角度来描述一件事情
2.3自旋锁和挂起等待锁 自旋锁是轻量级锁的一种典型实现一般搭配while循环在加锁成功后退出循环但是加锁不成功后会进入循环再次尝试加锁 挂起等待锁是重量级锁的一种典型实现在加锁失败后会进入阻塞状态直到获取到锁 自旋锁的使用一般用于锁冲突比较小的情况由于高速反复的尝试加锁导致CPU的资源消耗上升取而代之的是加锁的速度快但是在线程多的情况下会发生“线程饿死”的问题
挂起等待锁的使用一般用于所冲突比较大的情况由于进入阻塞后就是内核随机调度来进行执行要进行的操作增加导致加锁更慢了
synchronized锁这里的synchronized锁具有自适应的能力的例如在锁冲突情况比较严重的时候这里的synchronized就是悲观锁、重量级锁、挂起等待锁.....所以这里的synchronized是根据当时的锁的冲突情况来进行自适应的~~~
2.4普通互斥锁和读写锁 普通互斥锁即synchronized类似只有加锁和解锁 读写锁这里的解锁是一样的但是在加锁的时候分为两种即“加读锁”与“加写锁” 这里的情况就是
读锁和读锁这之间不会出现锁的冲突
读锁和写锁这之间会出现锁的冲突
写锁和写锁这之间会出现锁的冲突
即一个线程加读锁的时候另一个线程是可以“读”的但是是不可以“写”的
即一个现场加写锁的时候另一个线程是不可以进行“读”和“写”的
为什么要引入读写锁
在线程的读的操作中读这个操作本来就是线程安全的但是使用synchronized任然要给这一部分要加锁由于加锁这个操作本来就是一个低效率的操作在读的过程中不加锁可以打打提升效率
但是读的时候完全不加锁可能会在读的时候进行写操作所以这里又要加“读锁”
2.5公平锁和非公平锁 公平锁即等待加锁的时间越久就应该在锁释放的时候先加上锁先来先到原则 非公平死锁即在锁释放后获取锁的概率是一样的存在竞争 如下图所示 2.6可重入锁和不可重入锁 可重入锁即在加锁过后任然在这个线程继续进行加锁 不可重入锁即在加锁过后这个线程就不能进行加锁了 例如synchronized是一个可重入锁但是在系统中的锁是一个不可重入锁
可重入锁需要记录加锁的对象以及加锁的次数
️3.synchronized的加锁过程
在synchronized加锁之前会经历一下升级过程
3.1锁升级
1.偏向锁阶段
这里的偏向锁阶段的实现和之前讲解的“懒汉模式”是有一定的联系的即非必要不加锁但是这里的偏向锁并不是真正意义上的加锁 偏向锁即一种非必要不加锁的模式真正意义上是不加锁的而是进行一次轻量级的标记 这里就是当没有锁进行竞争的话就会不加锁只是轻量化标记一下当有锁的竞争那么这个标记的就会很快拿到锁
偏向锁的作用 存在锁冲突的情况下这中锁没有提高效率但是当没有锁的竞争后因为只是轻量化标记而不加锁那么这里的效率就会得到很大的提升
2.轻量级锁 轻量级锁即通过自旋的方式进行实现反复快速的进行加锁的操作 优点在锁的释放后能够快速的拿到并加上锁
缺点非常消耗CPU的资源
这里synchronized会根据有多少个线程在参与竞争如果比较多那么就会升级成重量级锁
3.重量级锁 重量级锁即拿不到锁的线程不会进入自旋状态而是进入阻塞状态释放CPU资源 最后由内核进行随机调度从而加上锁
3.2锁消除 锁消除即synchronized的一种优化策略但是比较保守 即编译器在编译的时候优化一下两种情况
1.不存在锁竞争只有一个线程那么此时就会进行锁消除
2.加锁的代码中没有涉及到成员变量的改动只有局部变量的改动就不需要进行加锁
3.3锁粗化 锁粗化即讲一个细粒度的锁转化为一个粗粒度的锁 粒度即在synchronized{ }这个括号里的代码越少即粒度越细代码越多即粒度越粗
所谓的粒度粗化如下图所示 可以发现这里频繁的加锁解锁会造成额外的时间开销而直接一步到位可以剩下这部分的时间开销
️4.CAS的实现原理
4.1CAS的内部逻辑
所谓的CAS即compare and swap即一种比较和交换这是一个特殊的CPU的指令
其内部伪代码
boolean CAS(address,expectValue,swapValue){if(addressexpectValue){addressswapValue;return true;}return false
} 注意这里是一段伪代码不能进行运行只是描述逻辑的即先进行判断寄存器的值和地址值是否相等相等就将另一个swap寄存器的值给地址值内存地址值
4.2CAS实现线程安全
CAS是CPU的一种指令操作系统又对这个指令进行了封装我们的Java又对操作系统的API进行了封装那么我们就可以进行使用啦
在之前我们在实现两个线程对count实现加法操作需要进行加锁但是有了CAS就可以不用进行加锁了
代码如下
public static AtomicInteger countnew AtomicInteger();public static void main(String[] args) throws InterruptedException {Thread t1new Thread(()-{for (int i 0; i 50000 ; i) {count.getAndIncrement();}});Thread t2new Thread(()-{for (int i 0; i 50000 ; i) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(最终count的值为count.get());}
这里就是通过使用原子类工具实现了没有加锁的仍然线程安全的代码
注意之前的count是三个指令在线程的随机调度中存在不同指令的穿插的情况导致线程安全问题但是getandincrement本来就是一个线程安全的指令就是一个指令天然就具有原子性
4.3CAS的原子性
在实现原子类的伪代码如下图所示 即起初内存中的值为valueoldvalue是寄存器中的值进入循环当比较成功那么就value的值就为value1了
当存在随机调度的时候 那么此时就会有以下操作
第一步执行右边线程的操作 第二步进行随机调度走的代码 注意在次比较发现内存和寄存器的值是不一样的了此时就会进行再次读取内存在次进行循环比较发现一样了就会加1跳出循环
代价这里的代价就是while循环造成的自旋CPU的消耗
️5.总结
本期小编讲解了关于不同锁的基本概念包括我们经常使用synchronized的加锁过程包含的“锁升级锁消除锁粗化”的一系列的操作以及CAS的实现和我们之前线程安全的代码的举例本篇主要是涉及到关于锁面试题~~~
~~~~最后希望与诸君共勉共同进步 以上就是本期内容了 感兴趣的话就关注小编吧。 期待你的关注~~~