线程安全的概念及原因

1.观察线程不安全

public class ThreadDemo {static class Counter {public int count = 0;void increase() {count++;}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);}
}

运行结果:

结果不像我们一开始预想的那样,两个线程执行increase方法得到的结果为100000

说明这个线程是不安全的

2.线程安全的概念

想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

3.线程不安全的原因

3.1修改共享数据

上面的线程不安全代码中,涉及到多个线程针对count变量进行修改,此时这个count是一个多个线程都能访问到的“共享数据”

count变量在堆上,因此可以被多个线程共享访问

3.2原子性

什么是原子性:

我们把一段代码想象成一间厕所,每个线程就是要进入这个厕所的人,如果没有任何机制保证,A进入厕所之后还没出来,B是否也可以进入厕所,打断A在厕所里的隐私,这个就不具备原子性

那我们应该如何解决这个问题呢?是不是只要给厕所加一把锁, A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。

有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

一条 java 语句不一定是原子的,也不一定只是一条指令

比如刚才我们看到的count++ ,其实是由三步操作组成的:

1. 从内存把数据读到 CPU

2. 进行数据更新

3. 把数据写回到 CPU

不保证原子性会给多线程带来什么问题

如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。

这点也和线程的抢占式调度密切相关. 如果线程不是 "抢占" 的, 就算没有原子性, 也问题不大.

3.3可见性

可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.

Java 内存模型 (JMM): Java虚拟机规范中定义了Java内存模型.

目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果.

线程之间的共享变量存在主内存 (Main Memory).

每一个线程都有自己的 "工作内存" (Working Memory) .

当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.

当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存.

由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 "副本". 此时修改线程 1 的工作内存中的值, 线程2 的工作内存不一定会及时变化.

1) 初始情况下, 两个线程的工作内存内容一致.

2) 一旦线程1 修改了 a 的值, 此时主内存不一定能及时同步. 对应的线程2 的工作内存的 a 的值也不一定 能及时同步. 

这个时候代码中就容易出现问题. 

此时引入了两个问题:

1.为啥要这么多内存?

2.为啥要这么麻烦的拷来拷去?

1) 为啥整这么多内存?

实际并没有这么多 " 内存". 这只是 Java 规范中的一个术语, 是属于 "抽象" 的叫法.

所谓的 "主内存" 才是真正硬件角度的 " 内存". 而所谓的 "工作内存", 则是指 CPU 的寄存器和高速缓存.

2) 为啥要这么麻烦的拷来拷去?

因为 CPU 访问自身寄存器的速度以及高速缓存的速度, 远远超过访问内存的速度(快了 3 - 4 个数量级,  就是几千倍, 上万倍).

比如某个代码中要连续 10 次读取某个变量的值, 如果 10 次都从内存读, 速度是很慢的. 但是如果只是第一次从内存读, 读到的结果缓存到 CPU 的某个寄存器中, 那么后 9 次读数据就不必直接访问内存了. 效率就大大提高了.

那么接下来问题又来了, 既然访问寄存器速度这么快, 还要内存干啥??

答案就是一个字:

值的一提的是, 快和慢都是相对的. CPU 访问寄存器速度远远快于内存, 但是内存的访问速度又远 远快于硬盘.

对应的, CPU 的价格最贵, 内存次之, 硬盘最便宜.

3.4代码顺序性

什么是代码重排序

一段代码是这样的:

1. 去前台取下 U 

2. 去教室写 10 分钟作业

3. 去前台取下快递

如果是在单线程情况下,  JVMCPU指令集会对其进行优化,比如,按 1->3->2方式执行,也是没问题,可以少跑一次前台。这种叫做指令重排序

编译器对于指令重排序的前提是 "保持逻辑不发生变化". 这一点在单线程环境下比较容易判断, 但是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.

4 解决之前的线程不安全问题

public class ThreadDemo {static class Counter {public int count = 0;synchronized void increase() {count++;}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);}
}

synchronized关键字在下一篇详细解释~

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

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

相关文章

Docker安装记录

老是报错 “Error response from daemon: Get “https://registry-1.docker.io/v2/”: proxyconnect tcp: dial tcp 127.0.0.1:7890: connect: connection refused” &#xff0c;不知道是什么原因&#xff0c;卸载了重装一下。 彻底卸载 sudo apt-get purge docker-ce docke…

进一步分析并彻底解决 Flink container exit 143 问题

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

Java并发编程之锁的艺术:面试与实战指南(三)

Java并发编程之锁的艺术&#xff1a;面试与实战指南&#xff08;三&#xff09; 文章目录 Java并发编程之锁的艺术&#xff1a;面试与实战指南&#xff08;三&#xff09;前言十七、Java中线程和进程的区别是什么&#xff1f;十八、什么是Java内存模型&#xff08;JMM&#xff…

AlibabaCloud微服务下的链路追踪系统实战详解

&#x1f680; 作者 &#xff1a;“二当家-小D” &#x1f680; 博主简介&#xff1a;⭐前荔枝FM架构师、阿里资深工程师||曾任职于阿里巴巴担任多个项目负责人&#xff0c;8年开发架构经验&#xff0c;精通java,擅长分布式高并发架构,自动化压力测试&#xff0c;微服务容器化k…

如何利用AI技术提升内容生产的效率和质量

目录 前言1 自动化内容生成1.1 文章生成1.2 视频制作1.3 音频合成 2 内容分发与推广2.1 智能内容推荐2.2 社交媒体管理 3 内容分析与优化3.1 用户反馈分析3.2 内容效果评估 结语 前言 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术对内容生产、分发和优…

Django框架之Ajax基础

一、JSON知识回顾 1、什么是JSON JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;通常用于在网络之间传输数据。它基于JavaScript的语法&#xff0c;但是独立于编程语言&#xff0c;因此几乎所有编程语言都支持解析和生成JSO…

2024第六届济南国际大健康产业博会将于5月27日如期开幕

由山东省城市经济学会、山东省科学养生协会主办的第六届中国&#xff08;济南&#xff09;国际大健康产业博览会&#xff0c;将于5月27-29日&#xff0c;在济南黄河国际会展中心盛大举办。 近年来&#xff0c;健康越来越受到大众的重视&#xff0c;在我国经济重要的转型阶段成…

Java Closeable 和 AutoCloseable接口

AutoCloseable & Closeable Closeable和AutoCloseable都是接口&#xff0c;且都只定义了一个close()方法。 Closeable: 定义于 java.io包中&#xff0c;于JDK5添加&#xff1b; AutoCloseable: 定义于java.lang包中, 于JDK7添加; AutoCloseable.java package java.lang…

AI 编程在哪些场景能够提高效率?

AI 编程在许多场景都能提高效率&#xff0c;其中一些主要场景包括&#xff1a; 1、自动化任务&#xff1a; AI 编程可以用于自动化重复性任务&#xff0c;如数据清洗、数据转换、文件处理等。通过机器学习和自然语言处理等技术&#xff0c;可以让计算机自动执行这些任务&#…

MFC实现点击列表头进行排序

MFC实现点击列表头排序 1、添加消息处理函数 在列表窗口右键&#xff0c;类向导。选择 IDC_LIST1&#xff08;我的列表控件的ID&#xff09;&#xff0c;消息选择LVN_COLUMNCLICK。 2、消息映射如下 然后会在 cpp 文件中生成以下函数 void CFLashSearchDlg::OnLvnColumnclic…

C++中的右值引用和移动语义

目录 1 左值引用和右值引用 2 左值引用与右值引用比较 3 右值引用使用场景和意义 4 右值引用引用左值及其一些更深入的使用场景分析 5 完美转发 6.常数右边引用 1 左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c…

顶级开源Kubernetes管理工具有哪些?好用Kubernetes工具推荐

Kubernetes已经成为容器编排领域颠覆性的技术&#xff0c;而充满活力的开源社区是其成功背后的推动力。本文将为大家推荐好用的Kubernetes工具&#xff0c;围绕Kubernetes发展的生态系统的广度和深度。 从自动化和监控到网络和安全性&#xff0c;这些工具为管理容器化应用程序…

数据库系统原理实验报告5 | 数据查询

整理自博主本科《数据库系统原理》专业课自己完成的实验报告&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 专业课本&#xff1a; ———— 本次实验使用到的图形化工具&#xff1a;Heidisql 目录 一、实验目的 二、实验内容 1.找出读者所在城市是“shangh…

最佳实践 | 八爪鱼采集器如何用PartnerShare做全民分销?

在数字化时代&#xff0c;数据采集和分析已经成为企业运营和决策的重要一环。八爪鱼采集器作为一款领先的SaaS产品&#xff0c;凭借其强大的数据采集和处理能力&#xff0c;成为了众多企业和个人用户的心头好。为了进一步拓展市场份额&#xff0c;提升品牌影响力&#xff0c;八…

Web 安全基础理论

Web 安全基础理论 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 短视频制作群&#xff1a; 744125867极安云…

第4章 Vim编辑器与Shell命令脚本

第4章 Vim编辑器与Shell命令脚本 1. Vim文本编辑器2. 编写Shell脚本2.2 接收用户的参数2.3 判断用户的参数 3. 流程控制语句3.1 if条件测试语句3.2 for条件循环语句3.3 while条件循环语句3.4 case条件测试语句 4. 计划任务服务程序复习题 1. Vim文本编辑器 Vim编辑器中设置了三…

云动态摘要 2024-05-09

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [免费试用]即刻畅享自研SaaS产品 腾讯云 2024-04-25 涵盖办公协同、营销拓客、上云安全保障、数据分析处理等多场景 云服务器ECS试用产品续用 阿里云 2024-04-14 云服务器ECS试用产品续用…

springcloud服务间调用 feign 的使用

引入依赖包 <!-- 服务调用feign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>创建调用外部服务的接口 需要使用的地方注入 使用 启动类增…

Filter 和 HandlerInterceptor 的执行顺序

Filter 和 HandlerInterceptor 的执行顺序 在 Spring 框架中&#xff0c;Filter 和 HandlerInterceptor 的执行顺序如下&#xff1a; Filter&#xff08;过滤器&#xff09;&#xff1a; Filter 是 Java Servlet 规范中定义的组件&#xff0c;用于在请求进入 Servlet 之前或响应…

python中<class ‘bytes‘>的流式数据如何解析及编码?

背景:需要将大模型的数据进行解析修改后再进行转发,也就是二次处理后返回。比如固定请求的字段,需要回传,真是笑死,你请求的啥字段不知道么?还要我再回传,浪费token和流量费。 示例如下: bdata: {"id":"162d8432fd31d3de9a0","object"…