当前位置: 首页 > news >正文

可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用

 一、💛

锁策略——接上一篇

6.分为可重入锁,不可重入锁

如果一个线程,针对一把锁,连续加锁两次,会出现死锁,就是不可重入锁,不会出现死锁,就是可重入锁。

如果一个线程,针对一把锁,连续加锁两次,如果产生了死锁,就是不可重入锁😄

public class Demo5 {public static void main(String[] args) {Thread t=new Thread(new Runnable() {int count=0;@Overridepublic synchronized void run() {    //加了一层锁synchronized (this){            //加了第二次锁count++;}System.out.println(count);}});t.start();}
}

那么我们来解释一下什么叫做死锁呢?

public synchronized void run() {    //加了一层锁
                synchronized (this){            //加了第二次锁
                    count++;
                }

这个代码中,调用方法先针对this加锁,此时假设加锁成功了,接下来到往下执行代码块中的this来进行加锁,此时就会出现锁竞争,this已经处于锁状态了,此时该线程就会阻塞~一直阻塞到锁被释放,才能有机会拿到锁。

这也是死锁第一个体现:this这个锁必须要run执行完毕,才能释放,但是要想执行完事,这个第二次加锁就应该加上,方法才可以执行,但是第二次想加上第一个就应该放锁,所以由于this锁没法释放,代码就卡在这里了,因此线程数量就僵住了。

还好synchronized是可重入锁,JVM帮我们承担了很多的任务

这里卡死就很不科学的一种情况,第二次尝试加锁的时候,该线程已经有了这个锁的权限了~~这个时候,不应该加锁失败,不应该进行阻塞等待的~

不可重入锁:这把锁不会保存,哪个线程加上的锁,只要他当前处于加锁状态之后,收到了‘加锁的请求’,就会拒绝当前加锁,而不管当下线程是哪个,就会产生死锁。 🌝

可重入锁:会让这个锁保存,是哪个线程加的锁,后续收到加锁请求之后,就会先对比一下,看看加锁的线程是不是当前持有自己这把锁的线程~~这个时候就可以灵活判定了。

那么该如何对比捏🌚

synchronized(this){synchronized(this){synchronized(this){
······->执行到这个代码,出了这个代码,刚才加上的锁,是否要释放?}       如果最里面的释放了锁,意味着最外面的synchronized和中间的synchronized后
}           续的代码部分就没有处在锁的保护之中了

真正要在这个地方释放锁,如加锁N层遇到了 } , JVM如何知道是最后一个呢,整一个整型变量,记录当前这个线程加了几次锁,每遇到一个加锁操作,计数器+1,每遇到一个解锁操作,就-1,当计数器减为0时,才真正执行释放锁操作,其他时候时不释放的。这一个思想就叫做‘引用计数’🐲🐲🐲(脑力+10000,人类进化不带我)

注补充:静态方法是针对类加锁,普通方法是针对this加锁


二、💙

死锁的详细介绍:两次加锁,都是同一个线程

死锁的三种典型情况;

1.一个线程,一把锁,但是不可入锁,该线程针对这个锁联系加两次就会出现死锁。

2.两个锁,两个锁,这两个县层先分别获取一把锁,然后再尝试分别获取对方的锁(

比如我拿了酱油要炫饺子,小杨拿醋,我让他把醋先给我,然后我一起给你,小杨一拍桌子,凭啥先给你,你多个啥?),如下图,双方陷入死循环中

 

public class Demo5 {public static Object  locker1=new Object();public static Object  locker2=new Object();public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic synchronized void run() {synchronized (locker1) {           //给1加锁System.out.println("s1.start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2) {    //没有放弃1的锁System.out.println("s2.over");}}}});t1.start();Thread t2=new Thread(new Runnable() {@Overridepublic synchronized void run() {synchronized (locker2) {         //給2加锁System.out.println("t2.start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker1) {     //没有放弃2的锁System.out.println("t1.over");}}}});t2.start();}
}

3.N个线程,M把锁:

(哲学家就餐问题)


 三、💜

如何应该避免死锁呢?先明确死锁产生的原因,死锁的必要条件(缺一不可)

1.互斥使用:一个线程获取到一把锁之后,别的线程不能获取到这个锁。(实际使用的锁,一般都是互斥的锁的基本特性)

2.不可抢占:锁只能被持有者主动释放,而不能是其他线程直接抢走(也是锁的基本特性)

3.请求和保持:这一个线程尝试获取多把锁,在获取第二把时候,会保持对第一把的获取状态(取决于代码结构)比如刚才写的,我只要让他获取完第一把再释放,在获取第二把,这样不发生冲突,但是可能会影响需求。

4.循环等待:t1尝试获取locker2,需要t2执行完释放locker2,t2尝试获取locker1,需要t1执行完毕,释放locker1(取决于代码结构)

我们的解决方式趋向于解决第四种(打破循环等待,如何具体实现解决死锁,实际方法有很多)

首先就是银行家算法(杀牛刀了属于,复杂,没必要会)

简单有效方法:针对锁进行编号,且规定加锁的顺序,只要线程加锁的顺讯,都严格执行上述顺序,就没有循环等待。

如下:

一般面试我们主动点:  问到死锁捡着了,细细的答,给他讲,让他觉得你是理解的

1.什么是死锁。           

2.死锁的几个典型场景

3.死锁产生的必要条件

4.如何解决死锁的问题

 


四、 ❤️

synchronized具体采用了哪些锁策略呢?

1.既是悲观锁,又是乐观锁

2.既是重量级锁,又是轻量级锁

3.重量级锁部分是基于多系统互斥锁实现的,轻量级锁部分是基于自旋锁实现的

4.synchronized是非公平锁(不会遵守先来后到,锁释放之后,哪个线程拿到锁个凭本事

5.synchronized是可重入锁(内部会记录哪个线程拿到了锁,记录引用计数)

6.synchronized不是读写锁

synchronized-内部实现策略(自适应)

讲解一下自适应:代码中写了一个synchhronized之后,可能产生一系列自适应的过程,锁升级(锁膨胀)

无锁->偏向锁->轻量级锁->重量级锁

偏向锁,不是真的加锁,而只是做了一个标记,如果有别的线程来竞争锁,才会真的加锁,如果没有别的线程竞争,就自始至终都不加锁了(渣女心态,没人来追你,我就钓鱼,你要是被追了,我先给你个身份,让别人别靠近你。)——当然加锁本身也有一定消耗

偏向锁在没人竞争的时候就是一个简单的(轻量的)标记,如果有别的线程来尝试加锁,就立即把偏向锁升级成真正加锁,让别人阻塞等待(能不加锁就不加锁)

轻量级锁-synchronized通过自旋锁的方式实现轻量级锁——这边把锁占据了,另一个线程按照自旋的方式(这个锁操作比较耗cpu,如果能够快速拿到锁,多耗点也不亏),来反复查询当前的锁状态是不是被释放,但是后续,如果竞争这把锁的线程越来越多了(锁冲突更加激烈了),从轻量锁,升级到重量级锁~随着竞争激烈,即使前一个线程释放锁,也不一定能够拿到锁,何时能拿到,时间可能比较久了会

 💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

锁清除:编译器,会智能的判断,当前这个代码,是否有必要加锁,如果你写了加锁,但实际没必要加锁,就会自动清除锁

如:单个线程使用StringBuffer编译器进行优化,是保证优化之后的逻辑和之前的逻辑是一致的,这样就会让代码优化变的保守起来~~咱们猿们也不能指望编译器优化,来提升代码效率,自己也要有作用,判断何时加锁,也是咱们非常重要的工作。

锁粗化:

关于锁的粒度,锁中操作包含代码多:锁粒就大

//1号       全写的是伪代码
和2号比较明显是2号的粒度更大
for(
synchronized(this){count++}
}//2号
synchronized(this){
for{
count++}
}

锁粒大,锁粒小各有好处:

锁粒小,并发程度会更高,效率也会更快

锁粒大,是因为加锁本身就有开销。(如同打电话,打一次就行,老打电话也不好)

上述的都是基本面试题


五、💚

CAS全称(Compare and swap) 字面意思:比较并且交换

能够比较和交换,某个寄存器中的值和内存中的值,看是否相等,如果相等就把另一个寄存器中的值和内存进行交换

boolean CAS(address,expectValue,swapValue){if(&address==expectValue){         //这个&相当于C语言中的*,看他两个是否相等&address=swapValue;             //相等就换值return true;                 
}return false;

此处严格的说是,adress内存的值和swapValue寄存器里的值,进行交换,但是一般我们重点关注的是内存中的值,寄存器往往作为保存临时数据的方式,这里的值是啥,很多时候我们选择是忽略的。

这一段逻辑是通过一条cpu指令完成的(原子的,或者说确保原子性)给我们编写线程安全代码,打开了新的世界。

CAS的使用

1.实现原子类:多线程针对一个count++,在java库中,已经提供了一组原子类

java.util.concurrent(并发的意思).atomic

AtomicInteger,AtomicLong,提供了自增/自减/自增任意值,自减任意值··,这些操作可以基于CAS按照无锁编程的方式来实现。

如:for(int i=0;i<5000;i++){

count.getAndIncrement();                         //count++

count.incrementAndGet();                        //++count

count.getAndDecrement();                      //count--

count.decrementAndGet()                      //--count

}

import java.util.concurrent.atomic.AtomicInteger;public class Demo6 {public  static AtomicInteger count=new AtomicInteger(0);    //这个类的初值呗public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for (int i=0;i<500;i++){count.getAndIncrement();}});Thread t2=new Thread(()->{for (int i=0;i<500;i++){count.getAndIncrement();}});t1.start();t2.start();t1.join();            //注意要等待两个线程都结束再开始调用t2.join();System.out.println(count);}
}

上述原子类就是基于CAS完成的

当两个线程并发的线程执行++的时候,如果加限制,意味着这两个++是串行的,能计算正确的,有时候者两个++操作是穿插的,这个时候是会出现问题的

加锁保证线程安全:通过锁,强制避免出现穿插~~

原子类/CAS保证线程安全,借助CAS来识别当前是否出现穿插的情况,如果没有穿插,此时直接修改就是安全的,如果出现了穿插,就会重新读取内存中最新的值,再次尝试修改。

部分源码合起来的意思就是 

public int getAndIncrement(){int oldValue=value;       //先储存值,防止别的线程偷摸修改之后,无法恢复到之前的值while(CAS(Value,oldValue,OldValue+1)!=true){  //检查是否线程被别的偷摸修改了//上面的代码是Value是否等于oldValue,假如等于就把Value赋值OldValue+1oldValue=value;                        //假如修改了就恢复了原来的样子}return oldValue;}

 

假如这种情况,刚开始设置value=0, 

CAS是一个指令,这个指令本身是不能够拆分的。

是否可能会出现,两个线程,同时在两个cpu上?微观上并行的方式来执行,CAS本身是一个单个的指令,这里其实包含了访问操作,当多个cpu尝试访问内存的时候,本质也是会存在先后顺序的。

就算同时执行到CAS指令,也一定有一个线程的CAS先访问到内存,另一个后访问到内存

为啥CAS访问内存会有先后呢?

多个CPU在操作同一个资源,也会涉及到锁竞争(指令级别的锁),是比我们平时说的synchronized代码级别的锁要轻量很多(cpu内部实现的机制) 

相关文章:

可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用

一、&#x1f49b; 锁策略——接上一篇 6.分为可重入锁&#xff0c;不可重入锁 如果一个线程&#xff0c;针对一把锁&#xff0c;连续加锁两次&#xff0c;会出现死锁&#xff0c;就是不可重入锁&#xff0c;不会出现死锁&#xff0c;就是可重入锁。 如果一个线程&#xff0c;针…...

JSON.parse()和JSON.stringify()用法

JSON.parse() 方法用于将 JSON 格式的字符串转换为 JavaScript 对象&#xff0c;而 JSON.stringify() 方法用于将 JavaScript 对象转换为 JSON 字符串。这两个方法可以组合使用来实现将数据从对象到字符串再到对象的转换。 示例 // 创建一个包含属性的 JavaScript 对象 var pe…...

Android 并发编程--阻塞队列和线程池

一、阻塞队列 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操作…...

Playwright快速上手-1

前言 随着近年来对UI自动化测试的要求越来越高&#xff0c;,功能强大的测试框架也不断的涌现。本系列主讲的Playwright作为一款新兴的端到端测试框架,凭借其独特优势,正在逐渐成为测试工程师的热门选择。 本系列文章将着重通过示例讲解 Playwright python开发环境的搭建 …...

PPT颜色又丑又乱怎么办?

一、设计一套PPT时&#xff0c;可以从这5个方面进行设计 二、PPT颜色 &#xff08;一&#xff09;、PPT常用颜色分类 一个ppt需要主色、辅助色、字体色、背景色即可。 &#xff08;二&#xff09;、搭建PPT色彩系统 设计ppt时&#xff0c;根据如下几个步骤&#xff0c;依次选…...

python计算相关系数R

方法一&#xff1a; import numpy as np# 计算相关系数R def r(y_true, y_pred):y_true np.array(y_true)y_pred np.array(y_pred)corr np.corrcoef(y_true, y_pred)[0][1]return corrcorr r(yture, ypred)方法二 import scipy.stats # 计算皮尔逊相关指数&#xff0c;并…...

黑马项目一阶段面试 自我介绍篇

面试官你好&#xff0c;我叫xxx&#xff0c;是来自xxxx的本科毕业生。我通过招聘网站/内推/线下招聘了解到的贵司&#xff0c;我具有扎实的Java后端的基础功底&#xff0c;基本掌握JavaSE、JavaEE流行技术的使用&#xff0c;并且我比较好学&#xff0c;心态也很乐观积极&#x…...

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测 目录 时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention时间序列预测&#xff0c;CNN-BiGRU-Attention结合注意力机制时…...

开发过程中遇到的问题以及解决方法

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 开发过程中遇到的问题以及解决方法 简单易用的git命令 git命令&#xff1a; 查看有几个分支&#xff1a;git branch -a 切换分支&#…...

本地oracle登录账号锁定处理,the account is locked

1.打开cmd命令窗口 2.打开sqlplus: sqlplus /nolog(加/nolog是不登录服务器的意思&#xff0c;不加就需要输账号密码) 3.切换到管理员&#xff1a;conn / as sysdba; 第2步第3步可以合并&#xff0c;直接使用sysdba登录&#xff1a;sqlplus / as sysdba; 4.解锁账号&#x…...

redission自定义hessian序列化

一。技术改造背景 由于之前的比较陈旧的技术&#xff0c;后面发起了技术改造&#xff0c;redis整体改后使用redisson框架。 二。问题 改造完成后&#xff0c;使用方反馈 缓存获取异常 异常信息如下 Caused by: java.io.CharConversionException: Unexpected EOF in the mid…...

P8642 [蓝桥杯 2016 国 AC] 路径之谜

[蓝桥杯 2016 国 AC] 路径之谜 题目描述 小明冒充 X X X 星球的骑士&#xff0c;进入了一个奇怪的城堡。 城堡里边什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡地面是 n n n\times n nn 个方格。如图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。 …...

oracle sql developer批量删除某个用户

随着navicate收费&#xff0c;还得破解&#xff0c;pl/sql developer配置麻烦&#xff0c;最近使用oracle sql developer来试试oracle的操作如何&#xff1b; 用着还行&#xff0c;没有卡顿现象&#xff0c; 最近要oracle sql developer批量删除某个用户下所有的表&#xff0…...

k8s 滚动更新控制(一)

在传统的应用升级时&#xff0c;通常采用的方式是先停止服务&#xff0c;然后升级部署&#xff0c;最后将新应用启动。这个过程面临一个问题&#xff0c;就是在某段时间内&#xff0c;服务是不可用的&#xff0c;对于用户来说是非常不友好的。而kubernetes滚动更新&#xff0c;…...

Java智慧工地APP源码带AI识别

智慧工地为建筑全生命周期赋能&#xff0c;用创新的可视化与智能化方法&#xff0c;降低成本&#xff0c;创造价值。 一、智慧工地APP概述 智慧工地”立足于互联网&#xff0c;采用云计算&#xff0c;大数据和物联网等技术手段&#xff0c;针对当前建筑行业的特点&#xff0c;…...

ME3116电源小板

最近设计一款PCB的时候使用微盟的dc dc电源ic踩了一个坑。 在使用me3116作为24v到5v的降压ic作为esp32系统前级的降压电路时&#xff0c;再没有铂电阻采样负载的情景下工作正常&#xff0c;带上负载后&#xff0c;ic工作不正常&#xff0c;过一段时间&#xff0c;后级电路会烧…...

摸准天气“小心思”,躲避恶劣天气“偷袭”

打开天气预报一看&#xff0c;天气真的很“善变”&#xff0c;既是高温又暴雨&#xff0c;偶尔还有台风路过&#xff0c;“蒸”的让人太太太难受了。看着天气在放晴和即将下雨之间“徘徊”&#xff0c;总是纠结带不带雨伞&#xff0c;让我的每次出门都成了一场冒险之旅。 持…...

Golang 局部变量、全局变量 声明

文章目录 一、局部变量二、全局变量 一、局部变量 四种声明方式 多变量声明&#xff1a; package mainimport "fmt"//局部变量声明 func main() {//方法一: 声明一个变量和数据类型&#xff0c;不初始化值&#xff1b;默认值为0&#xff1b;var lvA intfmt.Printl…...

软考高级之系统架构师之数据通信与计算机网络

概念 OSPF 在划分区域之后&#xff0c;OSPF网络中的非主干区域中的路由器对于到外部网络的路由&#xff0c;一定要通过ABR(区域边界路由器)来转发&#xff0c;既然如此&#xff0c;对于区域内的路由器来说&#xff0c;就没有必要知道通往外部网络的详细路由&#xff0c;只要由…...

牛客网华为OD前端岗位,面试题库练习记录01

题目一 质数因子 功能:输入一个正整数&#xff0c;按照从小到大的顺序输出它的所有质因子&#xff08;重复的也要列举&#xff09;&#xff08;如180的质因子为2 2 3 3 5 &#xff09; JavaScript Node ACM模式 const rl require("readline").createInterface({ i…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...