多线程(4)

发布时间:2026/11/7 1:23:49

多线程(4) 上一篇我们已经知道了synchronized加锁来解决线程安全问题修改操作不是原子的synchronized修饰普通方法相当于给this进行加锁synchronized修饰静态方法相当于给类对象加锁。synchronized-监视器锁 monitor lockJVM中采用的一个术语使用锁的过程中抛出一些异常可能会看到监视器锁这样的报错信息。1 synchronized的特性1互斥多个线程针对同一个对象加锁才会产生互斥锁冲突/锁竞争这个上一篇已经说过了2可重入要想理解可重入我们先来了解一下死锁的一种情况直接的代码示例在这段代码中第二次synchronized就得阻塞等待等到第一次加锁被释放第二次加锁的阻塞才会继续执行。看起来是两次一样的加锁没有必要但是在实际开发中很容易写出这样的代码比如这样的代码我们稍不注意就会写出来。这样的代码第一次加锁是可以成功的因为锁没有被使用第二次进行加锁锁是被占用的状态就得阻塞等待。要想解除阻塞需要往下执行才可以要想往下执行就需要等到第一次的锁被释放……这样的问题就成为“死锁”。值得注意的是这只是死锁的一种情况一个线程一把锁连续加锁多次。其他的情况稍后再说死锁是一个非常严重的bug会使代码执行到这一块之后就会被卡住。为了解决上述的问题Java的synchronized就引入了可重入的概念。可重入当某个线程针对一个锁加锁成功之后后续该线程再次针对这个锁进行加锁。不会触发阻塞而是继续往下走。但是如果是其他线程尝试加锁就会正常阻塞。可重入锁的实现原理关键在于让锁对象内部保存当前是哪个线程持有的这把锁。后续有线程针对这把锁加锁的时候进行对比看锁持有的线程是否和当前加锁的线程是同一个可以连续加锁多次最外层是真正加锁最外层也是真正解锁。站在JVM的视角看到多个}需要执行那它如何知道哪个}是真正解锁的那个会先引入一个变量计数器。每次触发{计数器每次触发}计数器--当计数器-- 为0的时候就是真正需要解锁的时候。那如何实现一个可重入锁呢1在锁内部记录当前是哪个线程持有的锁后续每次加锁都进行判定2通过计数器记录当前加锁的次数从而确定何时真正解锁2 关于死锁上面已经知道了一种情况一个线程一把锁连续加锁多次第二种情况两个线程两把锁每个线程获取到一把锁之后尝试获取对方的锁代码示例public static void main(String[] args) throws InterruptedException { Object locker1 new Object(); Object locker2 new Object(); Thread t1 new Thread(()-{ synchronized (locker1){ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (locker2){ System.out.println(t1线程两个锁都获取到); } } }); Thread t2 new Thread(()-{ synchronized (locker2){ try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (locker1){ System.out.println(t2线程两个锁都获取到); } } }); t1.start(); t2.start(); t1.join(); t2.join(); }值得注意的是必须是拿到第一把锁再拿第二把锁。必须是嵌套的关系这样的代码就构成了死锁那如果不加sleep,是否还会出现一样的现象呢那就得看具体的调度的顺序了。咱们加上sleep是为了确保t1拿到locker1t2拿到locker2等待1秒t1尝试拿locker2t2尝试拿locker1。如果不加sleep很可能t1一口气把locker1和locker2都拿了这个时候t2还没开动呢自然就构不成死锁。第三种情况N个线程M把锁一个经典的模型哲学家就餐问题哲学家相当于线程。筷子相当于锁。大部分情况下这个模型可以很好的运转只有在一些极端情况下会造成死锁。极端情况同一时刻大家都想吃面条同时拿起了左手边的筷子如上图所示此时任何一个线程都无法拿起右手边的筷子任何一个哲学家都吃不成面条那如何避免代码中出现死锁呢要想避免得先知道死锁是如何构成的构成死锁的四个必要条件重点1锁是互斥的一个线程拿到锁之后另一个线程在尝试获取锁就得阻塞等待2锁是不可抢占不可剥夺的线程1拿到锁线程2也尝试获取这个锁线程2必须阻塞等待而不是线程2直接把锁抢过来1和 2是锁的基本特性Java的synchronized是遵守这两点的3请求和保持一个线程拿到锁1之后在不释放锁1的情况下获取锁2在上面的哲学家的极端情况中如果先放下左手的筷子再拿右手的筷子就不会够成死锁那可能就说了代码中加锁的时候不去嵌套不就可以了。但是这种做法不是通用的有些时候就得嵌套4循环等待多个线程多把锁之间的等待过程构成了“循环”。A等待BB也等待A或者A等待BB等待CC等待A解决办法约定好加锁的顺序就可以破除循环等待了比如约定每个线程加锁的时候永远是先获取序号小的锁后获取序号大的锁代码示例运行结果避免死锁上述1和 2的情况是不可避免的3可以把嵌套的锁改为并列的锁4对加锁的顺序作出约定3 内存可见性是造成线程安全问题的原因之一代码示例运行结果我们可以看到虽然输入了非0的值但是此时t1线程循环并没有结束。很明显这是个bug是线程安全问题。一个线程读取一个线程修改修改线程修改的值但并没有被线程读取到这就是内存可见性问题。要想知道内存可见性问题得先谈谈编译器优化我们知道程序员的水平是参差不齐的研究JDK的大佬们就希望通过让编译器 JVM对程序员写的代码自动的进行优化。本来程序员写的代码是xxxx编译器/JVM会在你原有逻辑不变的前提下对你的代码进行调整使你的程序效率更高。值得注意的是编译器虽然声称优化操作是能够保证逻辑不变。但是在多线程的程序中编译器的判断可能会出现失误可能导致优化后的逻辑和优化前的逻辑出现细节上的偏差对于cmp这样的指令条件跳转load是读内存操作cmp是纯cpu寄存器操作load的时间开销可能是cmp的几千倍。短时间之内这个循环就会循环很多次执行过程中JVM就能感知到load反复执行的结果好像是一样的。这时JVM心想我执行这么多次读flag的操作值始终都是0既然都是一样的结果何必要反复执行这么多次呢。这个flag的值取决于用户输入不知道用户过多久才能输入于是就把读内存的操作优化成读取寄存器的操作把内存的值读到寄存器了后续再load就不再重新读内存了而是直接从寄存器里取。于是等到很多秒之后用户真正输入新的值真正修改flag此时t1线程就感知不到了编译器优化使得t1线程的读操作不是真正读内存如果我们微调上述代码就会得到不一样的结果代码示例运行结果本来这个while循环转的飞起1秒钟转几千万次……但是加了sleep(1)之后循环次数大幅度下降了当引入sleep之后sleep消耗的时间相比于上面的load flag的操作就不知道高了多少了。但是针对内存可见性问题不能指望通过sleep来解决因为使用sleep会大大影响到程序的效率。那该如何解决呢在语法中引入了volatile这个关键字通过这个关键字来修饰某个变量此时编译器通过对这个变量的读取操作就不会优化成读寄存器。代码示例运行结果OK啦到此结束
延伸阅读

更多相关文章

2026/10/25 8:25:15

Linux基础开发工具:从软件安装到程序编译全攻略

🌈个人主页:ꪔ小林Y ✨个人专栏:《C小白闯关日记》,《C语言小白闯关日记》,《数据结构入门——从原理到实战》,《拾光Linux》 🍀代码信条:每一行代码都是成长的脚印👣,每…

2026/11/6 23:09:43

Runway Solaris:界面世界模型如何为GUI Agent提供“预演”能力?

如果你一直在关注最近一两年的 AI Agent,应该见过不少“能自动操作电脑”的演示视频。模型截一张屏幕截图,把按钮和输入框识别出来,然后像人一样移动鼠标、敲键盘、完成任务。但这个方向真正落地时,最大的瓶颈往往不是模型不会点击…

2026/11/7 1:23:13

电工证焊工证考什么与多少钱,外省证怎么转过来全讲明白了

电工证焊工证考什么与多少钱,外省证怎么转过来全讲明白了 之前考的证在外省能不能转过来?这几乎是每个想考电工证、焊工证的朋友最先问的问题。别急,今天咱们就从头聊到尾,讲清楚 电工证焊工证考什么 ,到底要花 多少钱…

2026/11/7 1:23:13

电工证考试资料怎么考?证书过期了不知道怎么复审延期怎么报名

电工证考试资料怎么考?证书过期了不知道怎么复审延期怎么报名 证书过期了不知道怎么复审延期,这事儿真急人。前两天我接了个开封的学员电话,说他手里的电工证已经到期半年了,现在单位不认,想找人帮忙搞复审,结果一打听才发现流程比想象中复杂。这年头,…

2026/11/7 1:23:13

春招电工证行业前景与薪资待遇

新乡电工证春招避坑指南:怎么选含金量最高的证书? 不知道去哪报名怕被中介坑,这是很多想考电工证的兄弟心里最担心的事。电工证不是随便找个机构就能考的,也不是所有证书都能让你在工地、工厂、工程队里吃得开。含金量低的证,考了也是白考。今天从新乡过…

2026/11/7 1:23:12

神木考电工证在哪报名 全国通用电工证怎么考才稳妥

神木考电工证在哪报名 全国通用电工证怎么考才稳妥 实操考试心里没底怕挂科,是很多准备考电工证的朋友们最担心的问题。尤其是像我们这样在神木从事房建工程的电工,不仅需要持证上岗,还得确保考试一次通过,避免耽误工期。其实,只要了解清楚流程、准备充…

2026/11/7 1:23:12

电工作业触电事故家属怎么考特种作业证,考前押题怎么找

电工作业触电事故家属怎么考特种作业证,考前押题怎么找 工地太忙根本没时间复习考试,特别是像电工作业触电事故家属这类群体,平时工作已经够累,还要面对考试的压力,很多人干脆就放弃了。其实特种作业操作证是国家规定的强制性证件,不管你是出于工作需求…

2026/11/7 1:18:12

阳泉去哪里考电工证 全国通用证书怎么考

阳泉去哪里考电工证 全国通用证书怎么考 实操考试心里没底怕挂科,阳泉电工证报考流程不透明,报名材料清单不清楚,这些都成了很多想考电工证的学员心头的疙瘩。特别是对于初次接触特种作业操作证的人来说,考试内容、流程、材料准备、考试地点等都充满了未…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/6 10:49:01

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/7 0:03:08

郑州报考避坑指南:电工证和钳工证怎么选?实操考试心里没底怕挂科

郑州报考避坑指南:电工证和钳工证怎么选?实操考试心里没底怕挂科 实操考试心里没底怕挂科?你不是一个人。特别是在郑州这样的城市,很多施工企业负责人和一线作业人员在报考电工证和钳工证时都面临着同样的困惑。到底该考哪个证?哪个更适合自己?哪个证书…

2026/11/7 0:03:08

广西电工证办理流程全解析:外省证怎么转?在哪考最靠谱?

广西电工证办理流程全解析:外省证怎么转?在哪考最靠谱? 之前考的证在外省能不能转过来?这个问题我遇到太多了,今天就拿广西电工证办理流程来具体说说,别再走弯路了。广西电工证的报考、复审、继续教育这些环节,如果你不了解,真的会吃大亏,尤其对于从…

2026/11/7 0:03:08

电气电工证都有哪些保姆级教程

电气电工证都有哪些保姆级教程 你是不是正愁不知道去哪报名,怕被中介坑?别急,这篇保姆级教程专为河南初次报考人员准备,帮你搞清楚 电气电工证都有哪些 ,以及如何避开那些坑。 电气电工证都有哪些?别被五花八门的证书搞懵了…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/6 10:49:01

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑