【JavaEE初阶 — 多线程】线程安全问题 & synchronized

    c17b10e009894e5eaee2683a45eb4bfd.gif

8c16e702ece04cfa8e1d3f479a9f8dae.jpeg

f2c9147b8746449ebe6b05739a0d4f0b.gif

目录

1. 什么是线程安全问题  

(1) 观察线程不安全

(2) 线程安全的概念

2. 造成线程安全的原因  

(1)线程调度的随机性   

 问题描述  

 解决方案  

(2)修改共享数据&原子性问题 

问题描述

解决方案 

3.synchronized 关键字 

1. synchronized 的特性 

(1) 互斥  

 (2) 可重入  

 2. synchronized 的使用案例  

 (1)修饰代码块(明确指定锁哪个对象)

  锁任意对象  

  锁当前对象  

 (2)直接修饰普通方法:锁的 SynchronizedDemo 对象 

 (3)修饰静态方法:锁的 SynchronizedDemo类 的对象 

4.Java 标准库中的线程安全类


1. 什么是线程安全问题  


(1) 观察线程不安全


我们来看如下代码:

39e071f2a1f04a95a12ea1518b9a858b.png 代码逻辑:一个线程自增 5w 次,两个线程,总共自增 10w 次,预期结果:count=100000

632022d9ca2d4baea2774123923d8f34.png

预期结果与实际结果不符合的原因:t1,t2 和 主线程是并发执行的,调度是随机的。

实际结果 count=0 ,说明主线程是先打印了 count,t1,t2 才对 count 进行调整的 ;


为了让主线程等待 count 调整完毕再打印,我们在主线程中,通过 t1,t2 这两个对象的引用调用 join():

501f1f28e4c8486a83f6522b0096def1.png

此时,主线程 会在 t1,t2线程创建好后,进入阻塞状态,直到 t1,t2线程全部执行完毕,主线程才可以继续执行。多次运行程序,发现实际结果 与预期结果 又出现不同:

b95b57a31ffb4d2cbd8d0c4b993b3670.png

此时,我们修改调用 start() 和调用 join() 的顺序:

92fec91f026348aeb0ab45e8958d368e.png上述例子,说明了当前 bug (两个线程同时调整 count,预期结果与实际结果不相同),是由于多线程的并发执行所引起的

一段代码,在多线程中,如果并发执行产生bug了,那就称为 “线程安全问题” 或者叫做 “线程不安全”;

反之,如果一个代码,在多线程并发执行的环境下,也不会出现类似于上述的bug,这样的代码就叫“线程安全”。


(2) 线程安全的概念


想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:
如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。 


2. 造成线程安全的原因  


本文只讨论前三种产生线程安全问题的原因。


(1)线程调度的随机性   


  问题描述  


操作系统对于线程的调度是随机的(抢占式执行) 

操作系统对于线程调度是随机的,这是线程安全问题的罪魁祸首;

随机调度使一个程序,在多线程环境下,执行顺序存在很多的变数,因为这些变数,使得原来串行执行的代码,容易出现问题;因此,程序猿必须保证在任意执行顺序下,代码都能正常工作


  解决方案  


操作系统对于线程的调度是随机的 (抢占式执行),是操作系统的底层设定,我们无法左右该设定,所以要解决线程安全问题,我们应该从 产生线程安全问题的 第二,第三个原因 找突破口


(2)修改共享数据&原子性问题 


问题描述


多个线程修改共享数据,并且修改操作不是原子性的


对于多个线程修改共享数据,如当前的这个代码中,两个线程共同执行一个操作,那就是count++;

count++ 看起来是一行代码,但实际上它是对应到的,是三个CPU指令(修改操作不是原子性的);

2da4c3e075ed47baa037afcbf0ad1c36.png

并且是多个线程,并发执行这三个CPU指令

换言之,CPU 执行这三条指令的过程中,随时可能触发线程的调度切换

0adffb4c8caa42aca54a61618d4ac62e.png

 在 t1,t2 两个线程真正去进行 count++ 操作的时候,两个线程在 CPU 上执行,可能是并发,也可能是并行:

1daa4e267fc247728a11fdb8195f0273.png

所以多个线程,如果按照并发执行,就类似多个人玩同一台电脑上的MC(多个线程在同一个CPU上运行),每个人创建属于自己的世界种子(每个线程有不同上下文),每个世界种子的存档互不影响(不同线程的上下文彼此互不影响); 


既然两个线程有两个不同的上下文,也就是有两组不同的寄存器的值,我们就画两个CPU 寄存器来演示,两个线程并发执行 三步 CPU指令 的过程:

8733371be2a94522bd0fc8ec5919797b.png

对于这两个线程,并发执行这三个CPU指令,会出现的情况远远不止上图的两种情况;我们发现,在 t1线程执行 save指令 之前,如果 t2线程 执行了 load指令,那么就会出现值覆盖的问题

501f1f28e4c8486a83f6522b0096def1.png

对于上述代码,如果出现极端情况,甚至最后 count 的结果会小于 5w:

19b207ad5e524353953b87e207654485.png

而两个线程循环 5w 次,是为了让执行时间变长,更直观的查看线程安全问题。


 总结:

  • 当多个线程去进行并发这个逻辑的时候,产生中间结果会相互干扰
  • 一个线程可能加载了一个数据,它并非是另一个线程计算好的结果
  • 所以这就导致这两次结果,产生了相互覆盖的情况,所以这样的情况也就是造成线程安全问题的主要原因。

 解决方案 


操作系统随机调度是操作系统带来的解决不了;

多个线程对一个变量修改,有些可以规避,但有些根据需求无法规避。

所以,我们可以将 非原子性操作 改为 原子性操作,可以通过 synchronized 关键字加锁操作来实现。

锁属于系统提供的一个专门的机制,它能够产生互斥的效果。通过互斥效果,把本来是一个无序的并发执行,变成一个局部上的串行执行,从而进一步解决线程安全问题。



3.synchronized 关键字 


1. synchronized 的特性 


(1) 互斥  


互斥,又称为“锁冲突”或者“锁竞争”。

加锁本质上,就是把并发执行逻辑,给强行调整顺序,变成前后顺序的串行执行。

synchronized 的互斥特性,对强行修改逻辑执行顺序的操作,起到至关重要的作用。

通过 synchronized 的互斥效果,某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象synchronized 就会阻塞等待.

理解"阻塞等待":


针对每一把锁,操作系统内部都维护了一个等待队列。

当这个锁被某个线程占有的时候,其他线程尝试进行加锁,就加不上了,就会阻塞等待,一直等到之前的线程解锁之后,由操作系统唤醒一个新的线程,再来获取到这个锁。


注意:


  • 上一个线程解锁之后,下一个线程并不是立即就能获取到锁;而是要靠操作系统来"唤醒",这也就是操作系统线程调度的一部分工作。
  • 假设有ABC三个线程,线程A先获取到锁,然后B尝试获取锁,然后C再尝试获取锁,此时B和C都在阻塞队列中排队等待;
  • 但是当A释放锁之后,虽然B比C先来的,但是B不一定就能获取到锁,而是和 C重新竞争,并不遵守先来后到的规则

synchronized 用的锁是存在Java对象里的 ,而在 Java 中,任意一个对象,都可以作用于锁;

并且,锁对象的类型并不重要,重要的是,是否有多个线程尝试针对同一个对象加锁

所以我们通常都会定义 Object 类的锁对象:

  • 两个线程,针对同一个对象加锁,才会产生互斥效果;
  • 一个线程加上锁了,另一个线程就得阻塞等待,等到第一个线程释放锁,另一个线程才能枷锁;
  • 如果是不同的锁对象,不会产生互斥效果,线程安全问题,就不能得到解决,比如:
  • 两个线程针对两个不同锁对象加锁,此时这个锁等于没加,两个线程的锁并不会出现互斥的效果;
  • 或者一个线程加锁,一个线程不加锁,那这个加锁操作也是形同虚设的,这个锁不是写了synchronized,就好使了,而是多个线程针对一个锁对象加锁,才能真正产生配合

 (2) 可重入  


Java 中的 synchronized 是可重入锁;

在可重入锁的内部,包含了"线程持有者""计数器"两个信息。

synchronized是可重入的,每获取一次锁,计数器+1,释放锁时,计数器-1,直到计数器为0,锁才会真正释放。


如果某个线程加锁的时候,发现锁已经被人占用,但是恰好占用的正是自己,那么仍然可以继续获取到锁,并让计数器自增。

解锁的时候计数器递减为0的时候,才真正释放锁。(才能被别的线程获取到) 

  • 进入synchronized 修饰的代码块(进“ { ”),相当于 加锁;
  • 退出synchronized 修饰的代码块(出“ } ”),相当于 解锁。

这样的设定最大的好处是,避免单独写出的unlock,可能会执行不到,或者避免加锁后忘记释放锁的情况;

此时哪怕因为return,或者抛异常,结束的代码块的“ } ”,都能确保 synchronized 这个锁,被及时释放掉。


 2. synchronized 的使用案例  


 (1)修饰代码块(明确指定锁哪个对象)


  锁任意对象  


  锁当前对象  

哪个对象调用了 method(),this 就表示哪个对象。 


 (2)直接修饰普通方法:锁的 SynchronizedDemo 对象 


 (3)修饰静态方法:锁的 SynchronizedDemo类 的对象 

synchronized 修饰 static 方法,相当于针对这个方法当前所在的类进行加锁


4.Java 标准库中的线程安全类


Java 标准库中很多都是线程不安全的。

这些类可能会涉及到,多线程修改共享数据,却没有任何加锁措施:

但是还有一些是线程安全的.使用了一些锁机制来控制

拓展:


  • String类 中没有提供 public 修饰的方法,从而实现了“不可变”的特性,确保了线程安全;而 String类中的 final 用来实现 “不可继承” 的特性 。

  • String只是针对 对象进行读取操作,不涉及到线程安全问题,所以换句话说,String虽然没有加synchronized,但是天然就是线程安全的;
    因此,我们可以在多线程场景下,放心使用String类,这也是 String类 设计成不可修改的重要原因

   c17b10e009894e5eaee2683a45eb4bfd.gif

f2c9147b8746449ebe6b05739a0d4f0b.gif

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

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

相关文章

产品经理的重要性

一直觉得产品经理很重要,这几年写了好几篇和产品经理相关的思考。2020年写过对产品经理的一些思考的文章,2021年,写了一篇对如何分析项目的思考,2024年写了如何与PM探讨项目。 今天还想再写一篇,主要是最近很有感慨。…

Hunyuan-Large:推动AI技术进步的下一代语言模型

腾讯近期推出了基于Transformer架构的混合专家(MoE)模型——Hunyuan-Large(Hunyuan-MoE-A52B)。该模型目前是业界开源的最大MoE模型之一,拥有3890亿总参数和520亿激活参数,展示了极强的计算能力和资源优化优…

【Linux系列】利用 CURL 发送 POST 请求

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

通义灵码实操—飞机大战游戏

通义灵码实操—飞机大战游戏 有没有想象过自己独立编写一个有趣的小游戏。在本实践课程中,你不仅可以实现这个想法,而且还将得到通义灵码智能编程助手的支持与指导。我们将携手步入编程的神奇世界,以一种简洁、高效且具有创造性的方式&#…

lora训练模型 打造个人IP

准备工作 下载秋叶炼丹器整理自己的照片下载底膜 https://rentry.org/lycoris-experiments 实操步骤 解压整合包 lora-scripts,先点击“更新” 训练图片收集 比如要训练一个自己头像的模型,就可以拍一些自己的照片(20-50张,最少15张&…

Caffeine 手动策略缓存 put() 方法源码解析

BoundedLocalManualCache put() 方法源码解析 先看一下BoundedLocalManualCache的类图 com.github.benmanes.caffeine.cache.BoundedLocalCache中定义的BoundedLocalManualCache静态内部类。 static class BoundedLocalManualCache<K, V> implements LocalManualCache&…

Spring Boot框架下的教育导师匹配系统

第一章 绪论 1.1 选题背景 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;尽管身边每时每刻都在产生大量信息&#xff0c;这些信息也都会在短时间内得到处理&#xff0c;并迅速传播。因为很多时候&#xff0c;管理层决策需要大量信…

Unity SRP学习笔记(二)

Unity SRP学习笔记&#xff08;二&#xff09; 主要参考&#xff1a; https://catlikecoding.com/unity/tutorials/custom-srp/ https://docs.unity.cn/cn/2022.3/ScriptReference/index.html 中文教程部分参考&#xff08;可选&#xff09;&#xff1a; https://tuncle.blog/c…

2024年10款超好用的企业防泄密软件|企业文件加密防泄密必备!

随着信息技术的迅速发展&#xff0c;企业面临的数据泄露风险越来越高。为了保护企业的敏感信息&#xff0c;防止数据泄露&#xff0c;企业防泄密软件应运而生。以下是2024年值得关注的10款企业防泄密软件&#xff0c;帮助企业有效保障数据安全。 1.安秉网盾 安秉网盾防泄密是一…

K8S flannel网络模式对比

K8S flannel网络模式对比 VXLAN 模式Host-GW 模式如何查看 Flannel 的网络模式?如何修改 Flannel 的网络模式?如何修改flannel vxlan端口?Flannel 是一个 Kubernetes 中常用的网络插件,用于在集群中的节点之间提供网络连接。Flannel 提供了多种后端实现方式,vxlan 和 host…

计算机网络:网络层 —— 移动 IP 技术

文章目录 IPv6IPv6 的诞生背景主要优势IPv6引进的主要变化 IPv6数据报的基本首部IPv6数据报首部与IPv4数据报首部的对比 IPv6数据报的拓展首部IPv6地址IPv6地址空间大小IPv6地址的表示方法 IPv6地址的分类从IPv4向IPv6过渡使用双协议栈使用隧道技术 网际控制报文协议 ICMPv6ICM…

大客户营销数字销售实战讲师培训讲师唐兴通专家人工智能大模型销售客户开发AI大数据挑战式销售顾问式销售专业销售向高层销售业绩增长创新

唐兴通 销售增长策略专家、数字销售实战导师 专注帮助企业构建面向AI数字时代新销售体系&#xff0c;擅长运用数字化工具重塑销售流程&#xff0c;提升销售业绩。作为《挑战式销售》译者&#xff0c;将全球顶尖销售理论大师马修狄克逊等理论导入中国销售业界。 核心专长&…

【dvwa靶场:XSS系列】XSS (Stored)低-中-高级别,通关啦

更改name的文本数量限制大小&#xff0c; 其他我们只在name中进行操作 【除了低级可以在message中进行操作】 一、低级low <script>alert("假客套")</script> 二、中级middle 过滤了小写&#xff0c;咱们可以大写 <Script>alert("假客套…

css中pointer-events:none属性对div里面元素的鼠标事件的影响

文章目录 前倾提要当没有设置属性pointer-events时候结果 当子元素设置了pointer-events: none修改后的代码结果如下所示 当父元素设置了pointer-events: none若两个div同级也就是兄弟级 前倾提要 在gis三维开发的地图组件上放一个背景图片&#xff0c;左右两侧的颜色渐变等&a…

Vue:计算属性

Vue&#xff1a;计算属性 计算属性getset 在模板中&#xff0c;有时候填入的值要依赖于多个属性计算得出。 例如使用姓和名拼出全名&#xff1a; 以上效果可以通过以下代码实现&#xff1a; <div id"root">姓&#xff1a;<input type"text" v-m…

就业市场变革:AI时代,我们将如何评估人才?

内容概要 在这个充满变革的时代&#xff0c;就业市场正被人工智能&#xff08;AI&#xff09;技术深刻改变。随着技术的进步&#xff0c;传统的人才评估方式逐渐显示出其局限性。例如&#xff0c;过去依赖于纸质简历和面试评估的方式在快速变化的环境中难以准确识别真实的人才…

网站504错误出现的原因以及如何修复

504网关超时错误意味着上游服务器未能在规定时间内完成请求&#xff0c;导致无法传递网站内容。当您访问某个网站时&#xff0c;浏览器会向该网站的服务器发出请求。如果请求处理成功&#xff0c;服务器会返回200 OK状态码&#xff1b;但如果服务器响应超时&#xff0c;浏览器可…

学习RocketMQ(记录了个人艰难学习RocketMQ的笔记)

目录 一、部署单点RocketMQ 二、原理篇 三、实操篇 1、引入依赖 2、启动自动装配 3、配置application.yml 4、启动类 5、编写一个统一格式的消息对象 6、生产者 ​编辑 7、定义一个constant 8、多/单个消费者订阅一个主题 1.实现消费者 2.编写接口发送消息 3.接口…

安全关键型嵌入式系统设计模式整理及应用实例

本文提供了对安全关键型嵌入式系统设计模式的全面概述&#xff0c;这些模式旨在提高系统在面临潜在故障时的安全性和可靠性。文中详细介绍了15种设计模式&#xff0c;包括同质冗余&#xff08;HmD&#xff09;、异质冗余&#xff08;HtD&#xff09;、三模冗余&#xff08;TMR&…

京东零售推荐系统可解释能力详解

作者&#xff1a;智能平台 张颖 本文导读 本文将介绍可解释能力在京东零售推荐系统中的应用实践。主要内容包括以下几大部分&#xff1a;推荐系统可解释定义、系统架构、排序可解释、模型可解释、流量可解释。 推荐系统可解释定义 推荐系统可解释的核心包括三部分&#xff0…