Java并发编程实战 代码bug,Java并发编程实战(1)- 并发程序的bug源头

概述

并发编程一般属于编程进阶部分的知识,它会涉及到很多底层知识,包括操作系统。

编写正确的并发程序是一件很困难的事情,由并发导致的bug,有时很难排查或者重现,这需要我们理解并发的本质,深入分析Bug的源头。

并发程序问题的源头

为了提升系统性能,在过去几十年中,我们一直在不断的提升硬件的设计,包括CPU、内存以及I/O设备,但存在一个主要矛盾:三者之间速度有很大差异,CPU最快,内存其次,I/O设备最慢。

我们编写的程序,在运行过程中,上述三者都会使用到,在这种情况下,速度慢的内存和I/O设备就会成为瓶颈,为了解决这个问题,计算机体系结构、操作系统和编译程序做了如下改进:

CPU增加了缓存,以均衡与内存的速度差异。

操作系统增加了进程、线程以及分时复用CPU,从而均衡CPU与I/O设备的速度差异。

编译程序优化指令执行次序,使得缓存能够得到更加合理的利用。

并发程序的问题根源也基本来源于上述改进:

缓存引发的可见性问题

线程切换引发的原子性问题

编译优化引发的有序性问题

接下来我们分别展开描述。

缓存引发的可见性问题

什么是可见性?

可见性是说一个线程对共享变量的修改,另外一个线程能够立刻看到。

可见性问题是由CPU缓存引起的,它是在CPU变为多核后才出现的,单核CPU并不会存在可见性问题。

我们可以参考下面的示意图。

223cfc13643a

如图所示,当有2个线程同时访问内存中的变量x时,2个线程运行在不同的CPU上,每个CPU缓存都会保存变量x,线程运行时,会通过CPU缓存来操作x,那么当线程1进行操作后,线程2并不会立刻得到更新后的x,从而引发了问题。

我们来看下面的代码示例,它显示了对同一个变量使用多个线程进行加操作,最后判断变量值是否符合预期。

public class ConcurrencyAddDemo {

private long count = 0;

private void add() {

int index = 0;

while (index < 10000) {

count = count + 1;

index++;

}

}

private void reset() {

this.count = 0;

}

private void addTest() throws InterruptedException {

List threads = new ArrayList();

for (int i = 0; i < 6; i++) {

threads.add(new Thread(() -> {

this.add();

}));

}

for (Thread thread : threads) {

thread.start();

}

for (Thread thread : threads) {

thread.join();

}

threads.clear();

System.out.println(String.format("Count is %s", count));

}

public static void main(String[] args) throws InterruptedException {

ConcurrencyAddDemo demoObj = new ConcurrencyAddDemo();

for (int i = 0; i < 10; i++) {

demoObj.addTest();

demoObj.reset();

}

}

}

程序运行的结果如下。

Count is 18020

Count is 18857

Count is 16902

Count is 16295

Count is 54453

Count is 59475

Count is 56772

Count is 37376

Count is 60000

Count is 60000

我们可以看到,并不是每次返回的结果都是60000。

线程切换引发的原子性问题

什么是原子性?

一个或者多个操作在CPU执行的过程中不被中断的特性,被称为原子性。原子性可以保证操作执行的中间状态,对外是不可见的。

CPU可以保证的原子操作是在CPU指令级别的,并不是高级语言的操作符,而高级语言中的一个操作,可能会包含多个CPU指令。

以上述代码中的count = count + 1为例,它至少包含了三条CPU指令:

指令1:首先需要把变量count从内存加载到CPU寄存器。

指令2:在寄存器中执行+1操作。

指令3:将结果进行保存,这里可能会保存在CPU缓存,也可能保存在内存中。

上述指令执行过程中,可能会产生”线程切换“,如果多个线程同时执行相同的语句,那么因为线程切换,就会导致结果不是我们期望的。

原子性问题并不只在多核CPU中存在,在单核CPU中也是存在的。

编译优化引发的有序性问题

什么是有序性?

有序性是指程序按照代码的先后顺序执行。

编译器为了优化性能,有时候会改变程序中语句的先后顺序,一般情况下,这并不会影响程序的最终结果,但有时也会引发意想不到的问题。

我们以典型的单例模式为例进行说明,示例代码如下。

public class SingletonDemo {

private static SingletonDemo instance;

public static SingletonDemo getInstance() {

if (instance == null) {

synchronized(SingletonDemo.class) {

if (instance == null) {

instance = new SingletonDemo();

}

}

}

return instance;

}

}

一般情况下,假设有两个线程 A、B 同时调用 getInstance() 方法,他们会同时发现 instance == null ,于是同时对 Singleton.class 加锁,此时 JVM 保证只有一个线程能够加锁成功(假设是线程 A),另外一个线程则会处于等待状态(假设是线程 B);线程 A 会创建一个 Singleton 实例,之后释放锁,锁释放后,线程 B 被唤醒,线程 B 再次尝试加锁,此时是可以加锁成功的,加锁成功后,线程 B 检查 instance == null 时会发现,已经创建过 Singleton 实例了,所以线程 B 不会再创建一个 Singleton 实例。

但是,如果我们仔细分析getInstance()方法中的new操作,会发现它包含以下几步:

分配一块内存M。

在内存M上初始化SingletonDemo对象。

将M的地址赋值给instance变量。

但编译器可能会做一些优化,变成下面的样子:

分配一块内存M。

将M的地址赋值给instance变量。

在内存M上初始化SingletonDemo对象。

这样很可能导致线程 B获取instance之后,在instance初始化没有完全结束的情况下,调用它的方法,从而引发空指针异常。

上述是我们常见的并发程序的bug源头,只要我们能够深刻理解可见性、原子性和有序性在并发场景下的原理,很多并发bug就很容易理解了。

参考资料:

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

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

相关文章

ajax小结

转载于:https://www.cnblogs.com/infernoyy/p/7250548.html

linux mysql迁移,Linux 下安装MySQL并迁移备份

简单记录下在centOS上安装MySQL(MariaDB)的过程。这里我并没有选择特定的MySQL版本&#xff0c;使用的是源默认提供的版本。1. 执行安装命令&#xff1a;执行如下指令安装MySQL数据库。1yuminstall-ymysqlmysql-server注意这里同时安装了mysql和mysql-server。在输出一长串信息…

Asp.net--DropDownList控件绑定数据库数据

DropDownList控件绑定数据库数据&#xff1a;DB是公共类&#xff0c;reDt是公共类中的方法 放在Page_Load事件中 if (!IsPostBack){DB db new DB();DataTable dt db.reDt("select * from provinceInfo order by provinceid asc");ddlsmprovince.DataSource dt;ddl…

app推送以及提示音java,springboot 整合 Jpush 极光推送

产品简介&#xff1a;JPush 是经过考验的大规模 App 推送平台&#xff0c;每天推送消息数超过 5 亿条。 开发者集成 SDK 后&#xff0c;可以通过调用 API 推送消息。同时&#xff0c;JPush 提供可视化的 web 端控制台发送通知&#xff0c;统计分析推送效果。 JPush 全面支持 An…

C++游戏系列2:角色装备武器

很多其它见&#xff1a;C游戏系列文件夹 知识点&#xff1a;类的组合&#xff0c;A类的数据成员。是B类的对象&#xff0c;或B类的对象。做A类的数据成员    【项目-带武器的游戏角色】   设计一个武器类&#xff0c;其数据成员至少要有武器名、威力&#xff0c;还能够…

php 显示当前年月日时分秒,php 获取当前前后年、月、星期、日、时分秒的时间...

php 获取今天前后年、月、星期、日、时分秒的时间。通过设置时间戳函数strtotime()的参数获取时间。//获取当前时间echo date(Y-m-d H:i:s,strtotime(now));echo date(Y-m-d H:i:s);echo date(Y-m-d H:i:s,time());//获取5天前的时间echo date(Y-m-d H:i:s,strtotime("-5 …

Lydsy2017年4月月赛 抵制克苏恩

Description小Q同学现在沉迷炉石传说不能自拔。他发现一张名为克苏恩的牌很不公平。如果你不玩炉石传说&#xff0c;不必担心&#xff0c;小Q同学会告诉你所有相关的细节。炉石传说是这样的一个游戏&#xff0c;每个玩家拥有一个30 点血量的英雄&#xff0c;并且可以用牌召唤至…

php 判斷數據庫表是否存在,phpl判斷mysq數據庫中的某個數據表是否存在的方法總結...

注意&#xff1a;以下都是在ThinkPHP框架中進行測試方式1通過查詢MySQL的配置表信息//TABLE_SCHEMA&#xff1a;表示數據庫名 &#xff0c; TABLE_NAME : 表示表名$sql "SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMAdatabase_name and TABLE_NAME…

怎样学习(3):迭代学习,精益求精

古人云「十年寒窗无人问。一举成名天下知」&#xff0c;这是中国古代为数不多的读书人的真实写照。大多数读书人仅仅有十年寒窗&#xff0c;却不见得成名。 在软件开发领域有瀑布模式的软件project方法论。它将软开发的几个过程「需求分析&#xff0c;概要设计&#xff0c;具体…

matlab宏参赛,MATLAB杯无人机大赛 | 决赛通知!

原标题&#xff1a;MATLAB杯无人机大赛 | 决赛通知&#xff01;重磅消息——决赛通知&#xff01;经过近5个多月的准备&#xff0c;MATLAB杯无人机比赛即将迎来精彩的决赛&#xff0c;来自全国10强的参赛队伍&#xff0c;齐聚羊城广州&#xff0c;美丽的中山大学&#xff0c;进…

action中写php函数,WordPress中add_action(将函数连接到指定action)

WordPress中add_action(将函数连接到指定action)首先说下WordPress中用的钩子,将一个函数&#xff0c;以一个新的动作命名&#xff0c;该函数就是语法&#xff1a;123add_action($tag,$function_to_add,$priority,$accepted_args);?>参数&#xff1a;$tag(字符串)(必填)$fu…

selenium之截图

selenium支持对当前页面保存截图&#xff0c;使用方法&#xff1a; driver.get_screenshot_as_file(file_path) 代码举例&#xff1a; ...... def get_screenshot(driver, path):保存页面截图n 0if path:passelse:path "D:\\test_data\\screenshot\\error_png.png"…

java excel类库,jExcelApi Java 操作 Excel 的类库

jExcelApi Java 操作 Excel 的类库Java Excel API(JXL)是一个成熟&#xff0c;开源的用来操作Excel电子表格的Java类库&#xff0c;支持读取&#xff0c;修改&#xff0c;写入等操作。这个项目基于GPL发布,而且对中文有很好的支持。● 支持字体、数字、日期操作● 能够修饰单元…

锤子剪刀布 (20)

时间限制 1000 ms 内存限制 32768 KB 代码长度限制 100 KB 判断程序 Standard (来自 小小)题目描述 大家应该都会玩“锤子剪刀布”的游戏&#xff1a;现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入描述…

php表格js特效,JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】

本文实例讲述了JavaScript表格隔行变色和Tab标签页特效。分享给大家供大家参考&#xff0c;具体如下&#xff1a;最近一直在看JavaScript知识&#xff0c;偶尔也穿插一点Jquery&#xff0c;感觉Jquery用起来真爽&#xff0c;减少了很多的代码量&#xff0c;而且学习也不是很高。…

PHP 递归删除目录中文件

/** * 递归删除目录中文件 * param $pathname * return bool */public static function delDir($pathname)//要删除的目录{ if(file_exists($pathname)) { if(is_file($pathname)) { unlink($pathname); } else { $dir opendir($pathn…

matlab 符号表达式 系数 小数,matlab符号表达式系数

1数学符号matlab符号表达式系数: 数学符号&#xff0c;缺失&#xff1a;matlab表达式系数2838/9数学符号归类&#xff0c;包含运算符号、比较符号、几何符号、代数符号、常用分数、积分等各种符号。数学符号&#xfe62;&#xfe63;/≌∽≦≧≒&#xfe64;&#xfe65;≈≡≠…

STM32视频教程《原子教你玩STM32》

源&#xff1a;STM32视频教程《原子教你玩STM32》转载于:https://www.cnblogs.com/LittleTiger/p/7267735.html

海豚php表格,表格选取(1.4.3+) · DolphinPHP1.5.0完全开发手册-基于ThinkPHP5.1.41LTS的快速开发框架 · 看云...

## addSelectTable(name值, 标题, 提示, 表格列头 , 表格数据, 选择内容的url])标识符&#xff1a;selectTable>[info]1.4.3或更高版本支持。>[danger] **注意&#xff1a;该功能仅1.4.3版本以上&#xff0c;并且授权版用户才有&#xff0c;普通版没有该功能**。如果您是…

Number Sequence (KMP的应用)

个人心得&#xff1a;朴素代码绝对超时&#xff0c;所以要用到KMP算法&#xff0c;特意了解了&#xff0c;还是比较抽象&#xff0c;要多体会 Given two sequences of numbers : a11, a22, ...... , aNN, and b11, b22, ...... , bMM (1 < M < 10000, 1 < N < 1000…