JAVAEE多线程——锁

文章目录

  • 什么是锁
    • 为什么需要锁
    • 如何加锁
    • synchorized 的使用
      • synchronized 修饰方法
      • synchronized 修饰代码块
  • 死锁问题
    • 那种场景会造成死锁
      • 死锁的本质
      • 由于内部存在无限循环导致的死锁
    • 死锁的第二种情况
    • 哲学家吃饭模型
    • 造成死锁的必要条件

什么是锁

首先我们来解释一下什么是锁呢?JAVA中的锁其实严格来说更像是一个门,我们在进行某个代码操作的时候如果先对这个代码上锁那么就会使得在一个时间内只有一个线程进行这类代码,最终的结果就是当我们对一个资源做修改的时候只有一个线程在进行。

为什么需要锁

为什么需要锁呢?我们之前举过一个列子,代码如下

public class Main {public static int count=0;public static void main(String[] args) {Thread t1=new Thread(()->{for(int i=0;i<5000;i++){count++;}});Thread t2=new Thread(()->{for(int i=0;i<5000;i++){count++;}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}try {t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(count);}
}

按照这个代码的运行我们预测的结果应该是1万的。那么最终他的运行结果到底是多少呢?
在这里插入图片描述
很明显这个代码最终的结果并不像我们想的那般。但是如果加上锁呢?

如何加锁

synchorized 如何加锁呢?我们要用到加锁的关键字,也就是synchorized当然了还有lock但是呢我们这篇文章主要围绕的synchorized 那么这个关键字如何使用的呢,首先他有两种使用方法第一种就是对方法进行修饰,第二种就是对代码块加锁 加锁也可以说成是加了一个同步监视器,

synchorized 的使用

这个关键字的使用其实是要求修饰某个对象,然后以这个对象为一种参照,我们可以这样理解那就是synchorized表示对某个代码块关门上锁,而synchorized小括号里修饰的那个对象就是这扇门的钥匙,我们想要进行这个代码修改这部分资源就必须获得这个钥匙才可以,那么如何获得呢?就是用synchorized(对象) 这样即可。例如以下代码

synchronized 修饰方法

class myrun{public static int count=0;public synchronized void run(){count++;}
}

这里面synchronized 就是对一个方法进行修饰了,这时候有人可能会疑惑认为这不对啊,因为上面说了synchorized需要一个任意类型的对象但是为什么对这个方法进行修饰的时候我们就没有像上面说的再加个括号里面随便写一个某个类的对象呢?其实很简单就可以想到的,因为我们这是对类里的某个方法进行修饰的因此我们的synchorized所修饰的需要的那个对象其实就是调用这个方法的那个对象。
那么除了这种办法外还有没有别的办法呢?

synchronized 修饰代码块

synchronized修饰代码块就跟我们上面说的格式一样了就是需要一个任意类型的对象当作钥匙进行修饰我们以上面那个bug为例如果加锁上面这个代码会变成什么样子呢?

public class Main {public static int count=0;public static Object ob=new Object();public static void main(String[] args) {Thread t1=new Thread(()->{synchronized (ob){for(int i=0;i<5000;i++){count++;}}});Thread t2=new Thread(()->{synchronized (ob){for(int i=0;i<5000;i++){count++;}}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}try {t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(count);}
}

上面的代码我们可以看出来,当我们将其加锁后我们可以发现原本应该并行的代码变成了串行执行的了,也正因为如此我们的程序才可以运行成功。那么为什么会如此呢?
我们说的加锁加锁,指的到底是什么呢?我是这么理解的,所谓加锁相当于是找了一个对象作为一个证明类似于门禁卡
比如说有个线程A和B他们都想获取某个资源但是这个资源在一个时间内只能一个人获取如下图
在这里插入图片描述
这时候A和B就达成了一个协议
在这里插入图片描述
于是呢两个人从刚开始的争取这个协议就变成了争取这个门禁卡然后获得这个门禁卡的人才可以对这个资源进行修改。
而没获得门禁卡的人只能在外面等候,等待对方用完后才可以轮到自己就像上厕所一样没有抢到坑位的人必须等里面的人上好厕所后才可以进去使用是一个道理。

死锁问题

那么上述情况说完后,我们来讲一下锁可能会出现的问题,其中最为常见的一个问题就是死锁问题我们的java的synchronized有三大特性如下

互斥:synchronized获取的锁只能在同一时刻被一个线程所使用不能同时被多个线程使用
刷洗内存:这一点的话说法比较多有人说可以有人说不行
可重入:这一点非常重要他避免了java的一种死锁方式如下代码

上面说的可重入避免了一种死锁是什么意思呢?请看如下代码

public class thread {public static Object ob=new Object();public static void main(String[] args) {Thread t1=new Thread(()->{synchronized (ob){synchronized (ob){System.out.println("打印成功");}}});t1.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

我们来看一下下面的这个代码,我们发现了一个问题那就是,我们的synchronized对一个对象连续的加锁了两次,那么按照正常的锁的互斥执行思路来看的话我们可以这样嘛?很明显是不可以的,因为当这把锁已经被加过锁之后想要再次加锁就必须要解锁,但是想要解锁又必须要让里面的代码块执行完毕这时候就出现了矛盾的问题,因此我们说这种情况是死锁,但是java会出现这种问题嘛很明显不会的,因为java满足了可重入这个特性因此是没事的。但是c++的话就不行了。

那种场景会造成死锁

那么Java可重入的话就不会造成死锁了吗?当然不是的。那还有那种情况会造成死锁呢?那么首先我们要先了解一下死锁的本质究竟是什么

死锁的本质

死锁的本质概括起来就是,由于某个线程拥有了一把锁并且由于代码问题导致这个线程无法释放自己手里的锁从而引起其余需要这把锁的线程永久性阻塞的问题就是死锁。
那么既然如此,除了上述问题外还有哪些情况会引起死锁呢?

由于内部存在无限循环导致的死锁

public class thread {public static Object ob=new Object();public static void main(String[] args) {Thread t1=new Thread(()->{synchronized (ob){while(true){System.out.println("打印成功");}}});Thread t2=new Thread(()->{synchronized (ob){while(true){System.out.println("打印成功");}}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

上述代码中t1线程获取了锁之后但是由于其内部有无限循环导致了他的锁无法释放从而导致t2线程无法获取到锁,由此导致造成了死锁问题。

死锁的第二种情况

两把锁两个线程一人一把。代码如下

public class thread {public static Object ob=new Object();public static Object ob1=new Object();public static void main(String[] args) {Thread t1=new Thread(()->{synchronized (ob1){try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (ob){while(true){System.out.println("打印成功");}}}});Thread t2=new Thread(()->{synchronized (ob){synchronized (ob1) {while(true){System.out.println("打印成功");}}}});t1.start();t2.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

在这个代码中我们可以看到首先
t1获得了锁ob1 t2获得了锁ob
当t1想要获取锁ob的时候 t2此时因为无法获取ob1导致无法释放
t2想要和获取ob1的时候t1又因为无法获取ob导致无法释放。
这就特别像一个例子那就是你想要打开家门,但是家门钥匙忘在车上了你想要打开车却又发现自己的车钥匙忘在了家里。那么这种解决办法和模型在下面,有一个哲学家吃饭模型

哲学家吃饭模型

首先给大家描述一下哲学家吃饭模型,大致意思就是有五个哲学家五根筷子,然后这些哲学家坐在一个圆桌子上,这时候要吃饭了每位哲学家都需要拿起身边的两双筷子如下图
在这里插入图片描述
这是当只有一个哲学家要吃饭的时候其实还好解决直接拿起一双筷子即可,但是当所有的哲学家都需要吃饭怎么办呢?这时候筷子就不够分了,那么这时候有什么解决办法呢?那么我们的解决办法就是让哲学家必须遵守一个规定那就是只能优先拿两边编号小的那双筷子之后才可以获取身边编号大的筷子,那么我们来看看这是怎么解决问题的呢?
在这里插入图片描述
由此便解决了线程死锁的问题了

造成死锁的必要条件

那么上面讲解了死锁我们也可以总结一下死锁的必要条件有哪些呢?

  1. 互斥使用:同一把锁只能在一个时间内被一个线程使用
  2. 不可抢占:没有获取到这把锁的线程不能强行干预只能等到该线程释放锁之后才能拿到
  3. 请求和保持:当一个线程嵌套上锁的时候那么当申请使用第二把锁的时候第一把锁不需要释放
  4. 循环等待:参考哲学家吃饭模型其实就是一个循环等待。
 如果下辈子还能遇见你,你是我三生三世换来的珍遇。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/759237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合Xxl-Job

一、下载Xxl-Job源代码并导入本地并运行 Github地址:GitHub - xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度平台XXL-JOB&#xff09; 中文文档地址:分布式任务调度平台XXL-JOB 1.使用Idea或Eclipse导入 2.执行sql脚本(红色标记…

机器学习_神经网络

文章目录 简介反向传播小结 简介 为了构建神经网络模型&#xff0c;我们需要首先思考大脑中的神经网络是怎样的&#xff1f;每一个神经元都可以被认为是一个处理单元/神经核&#xff0c;它含有许多输入/树突&#xff0c;并且有一个输出/轴突。神经网络是大量神经元相互链接并通…

计算机网络简答题:复试+期末

文章目录 1.计算机网络的功能:2.计算机网络的分类:3.主机间的通信方式:4.电报交换、报文交换、分组交换的区别:5.计算机网络的性能指标:6.0SI模型和TCP/IP模型:7.通信信通的方式:8.端到端的通信与点到点通信的区别:9.同步通信和异步通信:10.频分复用、时分复用、波分复用和码分…

使用Pygame做一个乒乓球游戏(2)使用精灵重构

本节没有添加新的功能&#xff0c;而是将前面的功能使用精灵类(pygame.sprite.Sprite) 重构。 顺便我们使用图片美化了一下程序。 看到之前的代码&#xff0c;你会发现代码有点混乱&#xff0c;很多地方使用了全局变量(global)。 本节我们将使用类进行重构。 Block(Sprite)…

NCV7428D15R2G中文资料PDF数据手册参数引脚图图片价格概述参数芯片特性原理

产品概述&#xff1a; NCV7428 是一款系统基础芯片 (SBC)&#xff0c;集成了汽车电子控制单元 (ECU) 中常见的功能。NCV7428 为应用微控制器和其他负载提供低电压电源并对其进行监控&#xff0c;包括了一个 LIN 收发器。 产品特性&#xff1a; 控制逻辑3.3 V或5 V VOUT电源&…

Spark-Scala语言实战(4)

在之前的文章中&#xff0c;我们学习了如何在scala中定义无参&#xff0c;带参以及匿名函数。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言…

四、HarmonyOS应用开发-ArkTS开发语言介绍

目录 1、TypeScript快速入门 1.1、编程语言介绍 1.2、基础类型 1.3、条件语句 1.4、函数 1.5、类 1.6、模块 1.7、迭代器 2、ArkTs 基础&#xff08;浅析ArkTS的起源和演进&#xff09; 2.1、引言 2.2、JS 2.3、TS 2.4、ArkTS 2.5、下一步演进 3、ArkTs 开发实践…

Verilog基础:always结构和initial结构

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 always和initial是Verilog中的核心&#xff0c;它们被称为结构(construct)&#xff0c;用于组织语句的执行方式。下面将分别对这两者进行阐述。 always结构 图1是…

弹框el-dialog title展示不下,鼠标hover显示tip

el-dialog title展示不下&#xff0c;鼠标hover显示tip <el-dialog:visible.sync"shows":close-on-click-modal"false"v-dialogDragwidth"520px"><template #title><div class"custom-title"><el-tooltipplaceme…

【文件操作和IO】

文件操作和IO 1.文件2. 硬盘上文件的目录结构3. 文件路径4. 文件重要分类&#xff1a;5. Java中操作文件5.1 Java对于文件操作的API5.2 Java中使用File类来进行文件操作5.3 File类属性5.4 构造方法5.5 方法&#xff1a; 6. 文件内容的读写 -- 文件流&#xff08;数据流&#xf…

Apache Superset

前言 最近在准备一个小的项目&#xff0c;需要对 Hive 的数据进行展示&#xff0c;所以想到了把 Hive 的数据导出到 MySQL 然后用 Superset 进行展示。 Superset 1.1 Superset概述 Apache Superset是一个现代的数据探索和可视化平台。它功能强大且十分易用&#xff0c;可对接…

kubesphere all in one部署Jenkins提示1 Insufficient cpu

原因 devops 至少一个cpu&#xff08;1000m&#xff09;&#xff0c;但是其他资源已经占用了很多cpu CPU 资源以 CPU 单位度量。Kubernetes 中的一个 CPU 等同于&#xff1a; 1 个 AWS vCPU 1 个 GCP核心 1 个 Azure vCore 裸机上具有超线程能力的英特尔处理器上的 1 个超线程…

RISC-V架构的三种特权模式如何切换

1、RISC-V的三种特权模式 特权模式功能描述机器模式&#xff08;M-mode&#xff09;具有最高特权等级&#xff0c;具有访问所有资源的权限&#xff0c;通常运行固件和内核用户模式&#xff08;U-mode&#xff09;权限要比M模式低&#xff0c;通常是用来运行操作系统内核管理员…

MyBatis3源码深度解析(十七)MyBatis缓存(一)一级缓存和二级缓存的实现原理

文章目录 前言第六章 MyBatis缓存6.1 MyBatis缓存实现类6.2 MyBatis一级缓存实现原理6.2.1 一级缓存在查询时的使用6.2.2 一级缓存在更新时的清空 6.3 MyBatis二级缓存的实现原理6.3.1 实现的二级缓存的Executor类型6.3.2 二级缓存在查询时使用6.3.3 二级缓存在更新时清空 前言…

2024年第六届区块链与物联网国际会议(BIOTC 2024)即将召开!

2024年第六届区块链与物联网国际会议&#xff08;简称&#xff1a;BIOTC 2024&#xff09;将于2024 年 7 月 19 日至 21 日在日本福冈召开&#xff0c;旨在为来自行业、学术界和政府的研究人员、从业者和专业人士提供一个论坛&#xff0c;就研发区块链和物联网的专业实践进行交…

简介:使用TensorFlow实现python简版神经网络模型

如果你想进一步深入AI编程的魔法世界&#xff0c;那么TensorFlow和PyTorch这两个深度学习框架将是你的不二之选。它们可以帮助你构建更加复杂的神经网络模型&#xff0c;实现图像识别、语音识别等高级功能。 模型原理&#xff1a;神经网络是一种模拟人脑神经元结构的计算模型&a…

python 基于 websocket 的简单将视频推流到网页

本来有一台设备是要搞成无线的形式的&#xff0c;设备的摄像头的数据可以在一台局域网连接的平板上查看&#xff0c;因为试着使用 RTMP 推流&#xff0c;感觉延时太大了&#xff0c;而 Webrtc 感觉有太麻烦了&#xff0c;所以一开始看到这篇文章使用 UDP 协议进行推流&#xff…

stable diffusion webui ubuntu 安装

1.git clone 下来 GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UIStable Diffusion web UI. Contribute to AUTOMATIC1111/stable-diffusion-webui development by creating an account on GitHub.https://github.com/AUTOMATIC1111/stable-diffus…

数据仓库相关概述

数据仓库概述 数据仓库概念 数据仓库是一个为数据分析而设计的企业级数据管理系统。数据仓库可集中、整合多个信息源的大量数据&#xff0c;借助数据仓库的分析能力&#xff0c;企业可从数据中获得宝贵的信息进而改进决策。同时&#xff0c;随着时间的推移&#xff0c;数据仓…

期刊如何反击一波可疑图像

出版商正在部署基于人工智能的工具来检测可疑图像&#xff0c;但生成式人工智能威胁着他们的努力。 期刊正在努力检测用于分析蛋白质和DNA的凝胶的操纵图像。图片来源&#xff1a;Shutterstock 似乎每个月都会有一系列针对研究人员的新高调指控&#xff0c;这些研究人员的论文…