并发——同步访问共享的可变数据
关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一段代码块。许多程序员把同步的概念仅仅理解为一种互斥的方式。即,当一个对象被一个线程修改的时候,可以阻止另一个线程观察到内部不一致的状态。
这种观点是正确的,但是它并没有说明同步的全部意义。如果没有同步,一个线程的变化就不能被其他线程看到。同步不仅可以阻止一个线程看到对象处于不一致的状态之中,它还可以保证进入同步方法或代码块的每个线程,都看到由一个同步锁保护的之前所有的修改效果。
你可能听说过,为了提高性能,在读或者写原子数据的时候,应该避免使用同步。这个建议是非常危险而错误的。虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是它并不保证一个线程写入的值对于另一个线程将是可见的。为了在线程间进行可靠的通信,也为了互斥访问,同步是必要的。这归因于 java 语言规范中的内存模型,它规定了一个线程所做的变化何时以及如何变成对其他线程可见。
如果对共享的可变数据的访问不能同步,其后果将非常可怕,即使这个变量是原子可读写的。
测试以下代码
public class StopThread {private static boolean stop;public static void main(String[] args) throws InterruptedException {System.out.println(LocalDateTime.now());Thread t = new Thread(()->{int i = 0;while(!stop){i++;}System.out.println(LocalDateTime.now());});t.start();Thread.sleep(1000);stop = true;}
}
看上去这段代码的意思就是1s 之后停止,但实际上,它根本不会停止;后台线程永远在循环
问题在于,没有同步,就不能保证后台线程什么时候会发现主线程修改了 stop 的值
想要修复的这个问题,可以加上同步操作
public class StopThread {private static boolean stop;private static synchronized void setStop(){stop = true;}private static synchronized boolean getStop(){return stop;}public static void main(String[] args) throws InterruptedException {System.out.println(LocalDateTime.now());Thread t = new Thread(()->{int i = 0;while(!getStop()){i++;}System.out.println(LocalDateTime.now());});t.start();Thread.sleep(1000);setStop();}
}
注意,在上面的修改中,写和读方法都添加了同步关键字。如果只有一个则可能无效,读和写是两个动作,都需要被同步,才能真正完成同步。
synchronized 关键字由java 自动帮我们处理为锁,实际上,针对上面的要求,我们可以用一个更简单高效的关键字来处理:volatile
也就是在stop 前加上 volatile 修饰,就可以了,不需要再用 同步处理读和写。volatile 不提供锁和互斥操作,它只保证内存可见性,这里我们需要的也就是内存可见性——任何线程所修改之后,立刻被其他线程所见。
所以,要正确区分 synchronized 和 volatile 。synchronized 提供互斥操作,也就是任意时刻,所修饰的代码块或方法只能有一个线程在执行。而volatile 只能保证可见,没有互斥性。
以下代码可以做个试验
private static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {for(int x =0;x<10;x++){new Thread(() ->{for(int i =0;i<1000;i++){count ++;}}).start();}Thread.sleep(2000); // 保证多线程已经结束System.out.println(count);
}
多次执行之后,你会发现,count 不一定是 10000,大概率是9000多。
因为 voletile 只可见,不提供互斥,而 count++ 实际上是3个动作,先读取count 值,然后+1 ,最后写回到count 值
所以,可能发生 2个(或多个)线程同时读取到 count 比如 6,然后同时在内存中计算出 7,最后都写回7.这样6经过2次自增,结果却是7 。多次循环下来,最后结果就是不到10000
这样的需求,就只能使用同步,或者锁, 或者 原子类。
综上,如果需要多线程,一定要注意互斥性。如果只需要可见,则需要 volatile 可见性。如果无法确认,多数时候,还是用互斥比较稳妥,至少保证程序不出错。
多线程编程中一定要注意,并且,这种bug 非常难以处理。在debug 环境下,往往无法复现真正的多线程中的大量并发,也就很难复现这样的错误。
相关文章:
并发——同步访问共享的可变数据
关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一段代码块。许多程序员把同步的概念仅仅理解为一种互斥的方式。即,当一个对象被一个线程修改的时候,可以阻止另一个线程观察到内部不一致的状态。…...
Docker网络模型(九)禁用容器网络
禁用容器网络 如果你想完全禁用容器上的协议栈,你可以在启动容器时使用 --network none 标志。在容器内,只有回环设备被创建。下面的例子说明了这一点。 创建容器 $ docker run --rm -dit \--network none \--name no-net-alpine \alpine:latest \ash通…...
JavaScript 教程---互联网文档计划
学习目标: 每天记录一章笔记 学习内容: JavaScript 教程---互联网文档计划 笔记时间: 2023-6-5 --- 2023-6-11 学习产出: 1.入门篇 1、JavaScript 的核心语法包含部分 基本语法标准库宿主API 基本语法:比如操作符…...
做好功能测试需要的8项基本技能【点工进来】
功能测试是测试工程师的基础功,很多人功能测试还做不好,就想去做性能测试、自动化测试。很多人对功能测试的理解就是点点点,如何自己不用心去悟,去研究,那么你的职业生涯也就停留在点点点上了。在这里,我把…...
在弹出框内三个元素做水平显示
最终效果图要求是这样: js代码: // 显示弹出窗口 function showPopup(node) {var popup document.createElement(div);popup.className popup;var inputContainer1 document.createElement(div);/* inputContainer1.className input-container1; */…...
纠删码技术在vivo存储系统的演进【上篇】
作者:vivo 互联网服务器团队- Gong Bing 本文将学术界和工业界的纠删码技术的核心研究成果进行了相应的梳理,然后针对公司线上存储系统的纠删码进行分析,结合互联网企业通用的IDC资源、服务器资源、网络资源、业务特性进行分析对原有纠删码技…...
如何实现APP自动化测试?
APP测试,尤其是APP的自动化测试,在软件测试工程师的面试中越来越会被问到了。为了更好的回答这个问题,我今天就给大家分享一下,如何进行APP的自动化测试。 一、为了实现JavaAppiumJunit技术用于APP自动化测试,所以需要…...
INNODB和MyISAM区别
1 存储引擎是MyISAM 如下: CREATE table test_myisam (cli int ) ENGINEMyISAM 存储目录里会有三个文件 test_myisam.frm为“表定义”,是描述数据表结构的文件 test_myisam.MYI文件是表的索引 test_myisam.MYD文件是表的数据 2 存储引擎是INNODB…...
普中自动下载软件1.86下载程序失败案例
今天在用开发板做一个功能,下载的时候报错了,说芯片超时 确定驱动安装好了的 波特率也试了一圈 线也换过了 最后发现是芯片类型选错了,这个开发板是用的stc89c52,所以我选了图里这个,但是翻了开发板配套的资料,发现…...
JavaScript HTML DOM
JavaScript HTML DOM(文档对象模型)是一种用于访问和操作HTML文档元素的编程接口。它将HTML文档表示为一个树形结构,使开发人员可以使用JavaScript来操作和修改HTML元素、属性、样式和事件。 通过使用HTML DOM,你可以使用JavaScr…...
solr快速上手:配置IK中文分词器(七)
0. 引言 solr作为搜索引擎,常用在我们对于搜索速度有较高要求且大数据量的业务场景,我们之前已经配置过英文分词器,但是针对中文分词不够灵活和实用,要实现真正意义上的中文分词,还需要单独安装中文分词器 solr快速上…...
【软件测试】接口测试工具APIpost
说实话,了解APIpost是因为,我的所有接口相关的文章下,都有该APIpost水军的评论,无非就是APIpost是中文版的postman,有多么多么好用,虽然咱也还不是什么啥网红,但是不知会一声就乱在评论区打广告…...
第六章 假言:那么、就、则;才。
第六章 假言:那么、就、则;才。 第一节 假言-公式化转换-等价矛盾 真题(2012-38)-假言-A→B的公式化转换-A→B的等价命题:①逆否命题:非B→非A。 38.经理说:“有了自信不一定赢。”董事长回…...
[干货] 如何解决慢SQL?详细分析和优化实践!
慢SQL优化实践 本篇博客将分享如何通过慢SQL分析工具和常用优化手段,来解决慢SQL的问题。首先让我们看一下慢SQL的定义。 什么是慢SQL 简单来说,慢SQL指的是执行时间较长的SQL语句。在数据库中,一个查询的运行时间往往会受到多种因素的影响…...
数据库实验三 数据查询二
任务描述 本关任务:查询来自借阅、图书、读者数据表的数据 为了完成本关任务,你需要掌握: 如何多表查询 相关知识 查询多个数据表 在实际应用中,查询经常会涉及到几个数据表。 基于多个相关联的数据表进行的查询称为连接查询…...
论文笔记与实战:对比学习方法MOCO
目录 1. 什么是MOCO2. MOCO是干吗用的3. MOCO的工作原理3.1 一些概念1. 无监督与有监督的区别2. 什么是对比学习3. 动量是什么 3.2 MOCO工作原理1. 字典查找2. 如何构建一个好的字典3. 工作流程 3.3 (伪)代码分析 4. 其他一些问题5. MOCO v2和MOCO v35.1…...
大数据Doris(三十八):Spark Load 导入Hive数据
文章目录 Spark Load 导入Hive数据 一、Spark Load导入Hive非分区表数据 1、在node3hive客户端,准备向Hive表加载的数据 2、启动Hive,在Hive客户端创建Hive表并加载数据 3、在Doris中创建Hive外部表 4、创建Doris表 5、创建Spark Load导入任务 6…...
【Prometheus】mysqld_exporter采集+Grafana出图+AlertManager预警
前提环境:已经安装和配置好prometheus server 所有组件对应的版本: prometheus-2.44.0 mysqld_exporter-0.14.0 grafana-enterprise-9.1.2-1.x86_64.rpm alertmanager-0.25.0 prometheus-webhook-dingtalk-2.1.0 简介 mysql_exporter是用来收集MysQL或…...
softmax 函数
https://blog.csdn.net/m0_37769093/article/details/107732606 softmax 函数如下所示: y i exp ( x i ) ∑ j 1 n exp ( x j ) y_{i} \frac{\exp(x_{i})}{\sum_{j1}^{n}{\exp(x_j)}} yi∑j1nexp(xj)exp(xi) softmax求导如下: i j…...
【SpringMVC】拦截器和过滤器之间的区别
过滤器 拦截器 调用机制 基于函数的回调 基于反射机制(动态代理) 依赖关系 依赖Servlet容器 不依赖Servlet容器 作用范围 对几乎所有的请求起作用 只对action请求起作用 访问范围 不能访问action上下文、栈 可以访问action上下文、栈 action生命周期 中的调用次数…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
