多线程---线程安全问题及解决

文章目录

  • 一个线程不安全的案例
  • 造成线程不安全的原因
    • 抢占式执行
    • 多个线程修改同一个变量
    • 修改操作不是原子的
    • 内存可见性问题
    • 指令重排序问题
  • 如何让线程变得安全?
    • 加锁
    • volatile

一个线程不安全的案例

题目:有较短时间让变量count从0加到10_0000

解决方案:我们创建两个线程分别让count加5_0000次

结果:count < 10_0000


class Count{public int count = 0;public void increase(){count++;}}
public class Demo {//验证线程不安全问题public static void main(String[] args) {Count count1 = new Count();// 操作同一个变量Thread thread1 = new Thread(() -> {for (int i = 0; i < 50000; i++){count1.increase();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 50000; i++){count1.increase();}});thread1.start();thread2.start();try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}try {thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count1.count);   //  <100000}
}

造成线程不安全的原因

抢占式执行

操作系统调度线程的时候,是一个“随机”的过程,当两个线程都参与调度的时候,谁先谁后不确定。

多个线程修改同一个变量

线程之间是“并发执行的”,当多个线程修改同一个变量时,多个线程同时获取到了变量值。某一个线程修改了变量,修改的结果不能被其他线程知道,其他线程还会修改原先获取到的值,导致结果错误。

如果修改的是不同的变量,线程之间独立执行,不会出现问题。

修改操作不是原子的

count++操作底层是三条指令在CPU上完成的:

load:把内存中的值读到CPU寄存器中;
add:count+1;
save:把寄存器的值写回内存

由于这三条指令不是原子的,两个线程在执行时就会有不同的执行顺序:
在这里插入图片描述
在这些执行顺序下,都会使count没有正确的++,使最终结果出错。

内存可见性问题

JVM优化引入的BUG。例如,两个线程在操作同一个变量,一个线程读并且比较,一个线程修改。假设读操作非常频繁的情况下,比较操作也会非常的频繁。但是读是从内存中读,比较是在CPU里比较。比较的速度远远大于读的速度。而且每次读到的值还一样,这时编译器就会大胆优化:只读取一次,后面就不从内存中读了。每次比较都和前面读取到的值比较,不和内存中的值比较。这时另一个线程把内存中的值修改了但是这个线程比较的还时原来的值,就会有问题。

指令重排序问题

JVM优化引入的BUG。由我们自己写的代码在大多数情况下的执行流程中,指令的执行顺序往往都不是最优选择,即没有使运行速度达到最快。因此,JVM在编译时,就会在逻辑等价的前提下,对我们的指令进行重新排序使代码的运行速度变快。

这样的优化在单线程时,是没有问题的。但是在多线程的情况下,线程之间是抢占式执行的,哪条指令先执行哪条指令后执行不确定,就可能有问题。

如何让线程变得安全?

“抢占式执行”是线程调度的基本方式,我们无法干预。

“多个线程修改同一个变量”:我们在特定场景下就是得修改同一个变量,也无法改变。

“操作不是原子的”:我们保证线程安全的主要方式,通过synchronized加锁。

“内存可见性”“指令重排序”:JVM优化的问题。使用volatile解决

加锁

volatile

    public volatile int count = 0;

volatile只有一个用法就是修饰变量,表示该变量的值必须从内存中读取,不能从缓存中读取。
即:volatile禁止了编译器优化,避免了直接读取CPU寄存器中缓存的数据,而是每次都读取内存。

但是volatile并不能保证是操作是原子性的,因此,它只适合用于一个线程读,一个线程修改的场景。不适合用于两个线程都修改的场景。

谈到volatile就会联想到JMM(Java Memory Model):Java内存模型

在JMM中,引入了新的术语:
工作内存(work memory):即CPU寄存器(缓存)
主内存(main memory):真正读取的内存

站在JMM的角度看待volatile:
正常程序的执行过程中,先会把主内存的数据加载到工作内存中,再进行计算处理。编译器优化可能会导致不是每次都会真正的读取主内存,而是直接读取工作内存中的缓存数据,就可能导致内存可见性问题。volatile起到的效果就是保证每次读取数据都是真的从主内存中重新读取。

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

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

相关文章

myTracks for Mac:GPS轨迹记录器的强大与便捷

你是否曾经在户外活动或旅行中&#xff0c;希望能够记录下你的移动轨迹&#xff1f;或者在工作中&#xff0c;需要跟踪你的行程路线&#xff1f;myTracks for Mac 是一款强大的 GPS 轨迹记录器&#xff0c;它可以帮助你实现这些愿望。 myTracks 是一款专门为 Mac 设计的 GPS 轨…

[量化投资-学习笔记002]Python+TDengine从零开始搭建量化分析平台-MA均线的多种实现方式

MA 均线时最基本的技术指标&#xff0c;也是最简单&#xff0c;最不常用的&#xff08;通常使用EMA、SMA&#xff09;。 以下用两种不同的计算方法和两种不同的画图方法进行展示和说明。 MA 均线指标公式 MA (N)(C1 C2 C3 …C N )/N目录 方式一1.SQL 直接查询均值2.使用 pyp…

LLM系列 | 22 : Code Llama实战(下篇):本地部署、量化及GPT-4对比

引言 模型简介 依赖安装 模型inference 代码补全 4-bit版模型 代码填充 指令编码 Code Llama vs ChatGPT vs GPT4 小结 引言 青山隐隐水迢迢&#xff0c;秋尽江南草未凋。 小伙伴们好&#xff0c;我是《小窗幽记机器学习》的小编&#xff1a;卖热干面的小女孩。紧接…

BUUCTF zip伪加密 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 下载附件&#xff0c;得到一个zip压缩包。 密文&#xff1a; 解题思路&#xff1a; 1、刚开始尝试解压&#xff0c;看到了flag.txt文件&#xff0c;但需要解压密码。结合题目&#xff0c;确认这是zip伪加密&#…

Git Gui使用技巧

资料 https://www.runoob.com/w3cnote/git-gui-window.html 操作过程 创建仓库→添加远程仓库→扫描目录→文件移动→提交→上传 注意填注释 文件忽略 创建文件.gitignore→编写内容 *.log #文件 config.ini #文件 temp/ #目录

系列十八、请描述下bean的生命周期

一、概述 bean的生命周期是指bean从创建到销毁的整个过程。 二、生命周期 bean的生命周期是指bean从创建到销毁的整个过程&#xff0c;大致可以分为如下四个过程&#xff1a; 2.1、实例化 实例化可以通过如下几种方式完成&#xff1a;&#xff08;参考系列十五&#xff09…

【Vue3-Flask-BS架构Web应用】实践笔记1-使用一个bat脚本自动化完整部署环境

前言 近年来&#xff0c;Web开发已经成为计算机科学领域中最热门和多产的领域之一。Python和Vue.js是两个备受欢迎的工具&#xff0c;用于构建现代Web应用程序。在本教程中&#xff0c;我们将探索如何使用这两个工具来创建一个完整的Web项目。我们将完成从安装Python和Vue.js到…

2023年腾讯云2核4G配置服务器性价比怎么样?

2023年腾讯云2核4G配置服务器性价比怎么样?性价比高&#xff01;CPU具有100%计算性能&#xff0c;而且双11优惠价一年166元&#xff0c;三年566元&#xff0c;性价比超级高&#xff01; 2023腾讯云双11优惠活动&#xff1a;轻量2核4G5M服务器166.6元/年&#xff0c;3年566.6元…

Go学习第十一章——协程goroutine与管道channel

Go协程goroutine与管道channel 1 协程goroutine1.1 基本介绍1.2 快速入门1.3 调度模型&#xff1a;MPG模式介绍1.4 设置cpu数1.5 协程资源竞争问题1.6 解决协程并发方案 2 管道channel2.1 基本介绍2.2 快速入门2.3 管道的关闭和遍历2.4 管道和协程的结合2.5 声明 只读/只写 的管…

力扣:141. 环形链表(Python3)

题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的…

毅速丨增减材协同制造已逐渐成为趋势

近年来&#xff0c;增材制造3D打印技术的发展非常迅速&#xff0c;被广泛应用于航空航天、汽车、电子、医疗等许多行业。增材制造技术通过逐层增加材料的方式制造出各种复杂形状的零件&#xff0c;具有很高的制造效率和灵活性。 然而&#xff0c;在精密加工领域&#xff0c;增材…

双十一某宝、某东活动脚本

一、前言 双十一马上就快开始了&#xff0c;各大网购平台的优惠活动开展的如火如荼&#xff0c;羊毛党们也是摩拳擦掌&#xff0c;蠢蠢欲动。为了提高效率&#xff0c;自动化脚本应运而生&#xff0c;今天&#xff0c;小编为大家带来的就是这么三款自动化点击软件。主要是针对…