Java开发中遇到具有挑战的事_Java并发编程的挑战:遇到的问题及如何解决

并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行。在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题、死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战以及解决方案。

上下文切换

即使是单核处理器也支持多线程执行代码, CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以ICPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的时间片一般是几十毫秒(ms)。

CPU通过时间片分配算法来循环执行任务,当前任务执行-一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。

这就像我们同时读两本书,当我们在读一本英文的技术书时,发现某个单词不认识,于是便打开中英文字典,但是在放下英文技术书之前,大脑必须先记住这本书读到了多少页的第多少行,等查完单词之后,能够继续读这本书。这样的切换是会影响读书效率的,同样上下文切换也会影响多线程的执行速度。

多线程一定快吗

下面的代码演示串行和并发执行并累加操作的时间,请分析:下面的代码并发执行一定比串行执行快吗?

5530ba927b317d6c871e4cf634003fbf.png

上述问题的答案是“不一定”,测试结果如表所示。

1ae40d57c698b6caf51230e0e717e2eb.png

从表中可以发现,当并发执行累加操作不超过百万次时,速度会比串行执行累加操作要慢。那么,为什么并发执行的速度会比串行慢呢?这是因为线程有创建和上下文切换的开销。

测试上下文切换次数和时长

下面我们来看看有什么工具可以度量上下文切换带来的消耗。

使用I mbench31I可以测量上下文切换的时长。使用vmstat可以测量上下文切换的次数。Imbench3是一个性能分析工具。.

下面是利用vmstat测量上下文切换次数的示例。

72f128f2d09430fc7460505bcbe754ab.png

CS(Content Switch)表示上下文切换的次数,从上面的测试结果中我们可以看到,上下文每1秒切换1000多次。

如何减少,上下文切换

减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。

无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。

CAS算法:Java的Atomic包使用CAS算法来更新数据,而不需要加锁。使用最少线程:避免创建不需要的线程.比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

减少上下文切换实战

本节将通过减少线上大量WAITING的线程,来减少上下文切换次数。

第一步:用jstack命令dump线程信息,看看pid为3117的进程里的线程都在做什么。

65804b54107f9d51fc653c5bbb7efa80.png

第二步:统计所有线程分别处于什么状态,发现300多个线程处于WAITING(onobject-monitor)状态。

d40aa1199933b74ca9c338d8cb8dea4e.png

第三步:打开dump文件查看处于WAITING(onobjectmonitor)的线程在做什么。发现这些线程基本全是JBOSS的工作线程,在await。说明JBOSS线程池里线程接收到的任务太少,大量线程都闲着。

ec74c954f50b700e6778413cb6786119.png

第四步:减少JBOSS的工作线程数,找到JBOSS的线程池配置信息,将maxThreads降到100。

c31c83a3e070000f8307ebaf3ad6c0ab.png

第五步:重启JBOSS,再dump线程信息,然后统计WAITING(onobjectmonitor)的线程,发现减少了175个。WAITING的线程少了,系统上下文切换的次数就会少,因为每次从WATTTING到RUNNABLE都会进行一 次上下文的切换。读者也可以使用vmstat命令测试一 下。

eb6d4548c66c715669247c0428c99885.png

死锁

锁是个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解。但同时它也会带来-些困扰,那就是可能会引起死锁,一旦产生死锁,就会造成系统功能不可用。让我们先来看一段代码,这段代码会引起死锁。使线程t1和线程2互相等待对方释放锁。

3b85058ae3073d070b9e81e8fc3f2783.png

这段代码只是演示死锁的场景,在现实中你可能不会写出这样的代码。但是,在一些更为复杂的场景中,你可能会遇到这样的问题,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉。

一旦出现死锁,业务是可感知的,因为不能继续提供服务了,那么只能通过dump线程查看到底是哪个线程出现了问题,以下线程信息告诉我们是DeadI ockDemo类的第42行和第31行引起的死锁。

0a90c5e45f2bffbcbc3fdd8968152eef.png

现在我们介绍避免死锁的几个常见方法。

避免一个线程同时获取多个锁。避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。尝试使用定时锁,使用lock.tryLock(imeout)来替代使用内部锁机制。对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。资源限制的挑战

(1)什么是资源限制

资源限制是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。例如,服务器的带宽只有2Mb/s,某个资源的下载速度是1Ml/s每秒,系统启动10个线程下载资源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制。硬件资源限制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。软件资源限制有数据库的连接数和socket连接数等。

(2)资源限制引发的问题

在并发编程中,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间。例如,之前看到一段程序使用多线程在办公网并发地下载和处理数据时,导致CPU利用率达到100%, 几个小时都不能运行完成任务。后来修改成单线程,一个小时就执行完成了。

(3)如何解决资源限制的问题

对于硬件资源限制,可以考虑使用集群并行执行程序。既然单机的资源有限制,那么就让程序在多机上运行。比如使用ODPS、Hadoop或者自己搭建服务器集群,不同的机器处理不同的数据。可以通过“数据ID%机器数",计算得到一个机器编号,然后由对应编号的机器处理这笔数据。

如何在资源限制的情况下,让程序执行得更快呢?方法就是,根据不同的资源限制调整程序的并发度,比如下载文件程序依赖于两个资源带宽和硬盘读写速度。有数据库操作时,涉及数据库连接数,如果SQL语句执行非常快,而线程的数量比数据库连接数大很多,则某些线程会被阻塞,等待数据库连接。

总结

0d949cc95205ef0ae2edbea57680542b.png

以上介绍了在进行并发编程时,大家可能会遇到的几个挑战,并给出了一些解决建议。有的并发程序写得不严谨,在并发下如果出现问题,定位起来会比较耗时和棘手。所以,对于Java开发工程师而言,笔者强烈建议多使用JDK并发包提供的并发容器和工具类来解决并发问题,因为这些类都已经通过了充分的测试和优化,均可解决了本章提到的几个挑战。

当然,这些只是Java并发编程的冰山一角,完整的知识我已经整理成了一份文档。需要的转发文章关注私信“文档”获得领取方式。

4a10c583d92c9e176b034b9c048f46bc.png

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

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

相关文章

jQuery 属性选择器

jQuery 使用 XPath 表达式来选择带有给定属性的元素。 $("[href]") 选取所有带有 href 属性的元素。 $("[href#]") 选取所有带有 href 值等于 "#" 的元素。 $("[href!#]") 选取所有带有 href 值不等于 "#" 的元素。 $("…

使用的 SQL Server 版本不支持数据类型“datetime2”的错误解决方法

THE VERSION OF SQL IN USE DOES NOT SUPPORT DATATYPE ‘DATETIME2′ 主要错误原因,在使用ado.net entity的时候,entity使用的数据库是sqlserver 2008,或者sqlserver 2008 r2 但后来实际使用的数据库是sqlserver 2005, sqlserver…

树莓派(TCP客户端 )和Wemos(TCP服务端连接红外模块)通讯实现对红外设备的控制

参考:U如何用树莓派连接语音模块,红外模块来控制红外设备详解 作者:一只青木呀 发布时间:2020-08-12 17:14:10 网址:https://blog.csdn.net/weixin_45309916/article/details/107960066 目录硬件软件红外解码步骤1.连接…

java反编译微信小程序_教你如何一键反编译获取任何微信小程序源代码(图形化界面,傻瓜式操作)...

一键获取微信小程序源代码1 Tips:2   一键获取微信小程序源码, 使用了C#加nodejs制作 直接解压在D盘根目录下后就可以使用 将小程序文件放到 wxapkg目录下3 这个目录下有一些demo 可以先进行实验 使用正确 wxapkg exe这些文件应该在 D:CrackMinApp目录下4 然后打开…

PM2管理工具的使用

linux上PM2可以管理服务程序,防止程序无故关闭,具有程序守护功能,自动重启服务器程序,监控程序等好处,很方便,具体自己去体会! 官网地址: http://pm2.keymetrics.io/ 文档指南: ht…

C语言常用字符串操作函数大全详解(strstr,strtok,strrchr,strcat,strcmp,strcpy,strerror,strspn,strchr等)

参考:string.h中常用字符串操作函数说明(strstr,strtok,strrchr,strcat,strcmp,strcpy,strerror,strspn,strchr等) 作者:一只青木呀 发…

java将030A转换为方块_JAVA试题

1、下面关于变量及其作用范围的陈述哪个是不对的?( B )A.实例变量是类的成员变量。B.实例变量用关键字static声明。//Static 声明的是类变量C.在方法中定义的局部变量在该方法被执行时创建。D.局部变量在使用前必须被初始化。2、…

python 后缀 .py .pyc .pyw .pyo .pyd的区别

1.py是源文件 2.pyc是源文件编译后的文件: pyc是一种二进制文件,是由py文件经过编译后,生成的文件,是一种byte code,py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由…

MVC中使用Entity Framework 基于方法的查询学习笔记 (二)

解释&#xff0c;不解释&#xff1a; 紧接上文&#xff0c;我们在Visual Studio2012中看到系统为我们自动创建的视图(View)文件Index.cshtml中&#xff0c;开头有如下这句话&#xff1a; model IEnumerable<GuestBook.Models.Book> 这句话是MVC通过强类型获取数据的方式&…

位运算(按位与、按位或、异或、取反)以及原码、反码、补码

参考&#xff1a;运算符的计算&#xff08;按位与 按位或 异或 取反&#xff09; 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-07-23 18:13:55 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107543919 参考&#xff1a;计算机原码&#…

Docker03 Docker基础知识、Docker实战

1 Docker基础知识 1.1 什么是Docker Docker是一个可以装应用的容器&#xff0c;就像杯子可以装水、书包可以装书一样&#xff1b;docker官网 Docker是Docker公司开发的&#xff0c;并开源到GitHub上; Docker是跨平台的&#xff0c;支持windows、linux、Macos 1.2 Docker思想 1.…

Java字符串简化_关于java查询语句 如何简化的问题

首先&#xff0c;请你告诉我你使用PreparedStatement的理由&#xff0c;为什么不使用Statement&#xff1f;二者有什么区别&#xff1f;然后 你用到了PreparedStatement的特性了吗&#xff1f;接下来我们在探讨这代码该怎么写。下面的代码仅供参考&#xff0c;基本不具有实践意…

phpcms_v9推送到其他栏目后再在其他栏目删除导致数据库出错

修改phpcms/model/content_model.class.php大概454行的update_category_items()函数private function update_category_items($catid,$action add,$cache 0) {$this->category_db pc_base::load_model(category_model);if($actionadd) {$this->category_db->update…

Linux网络编程小知识(字节序、IP格式、函数、子网掩码、DNS域名解析代码实现)

参考&#xff1a;网络编程前的一些小知识–Linux笔记 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2021-04-12 23:19:10 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/115560197 参考&#xff1a;DNS域名解析 作者&#xff1a;一只青木呀 发布…

java ndk 在哪_NDK简介

一、NDK简介&#xff1a;C/C的动态库。Dalvik通过JNI编程方式调用C/C代码。NDK编程提高软件性能&#xff0c;加密保护APK文件ndk-build NDK编译生成脚本Java编译时会根据java声明文件生成dex文件&#xff0c;即使没有java代码NDK共享库&#xff1a;NDK生成的 .so文件(类…

ARM汇编基础详解(PS学习汇编的原因)

目录前言1.GNU 汇编语法2.Cortex-A7 常用汇编指令2.1 处理器内部数据传输指令&#xff08;内部寄存器数据非内存数据&#xff09;2.2 存储器访问指令&#xff08;RAM&#xff09;2.3 压栈和出栈指令&#xff08;了解&#xff09;2.4 跳转指令2.5 算术运算指令2.6 逻辑运算指令前…

java formatter()_Java Formatter locale()用法及代码示例

locale()方法是java.util.Formatter的内置方法&#xff0c;该方法返回语言环境。此区域设置由格式化程序构造设置。具有语言环境参数的该对象的format方法不会更改此值。用法&#xff1a;public Locale locale()参数&#xff1a;该函数不接受任何参数。返回值&#xff1a;如果未…

linux中tree命令

需要安装tree包(安装&#xff1a;yum -y install tree)。 tree命令的选项说明如下&#xff1a; 【 匹配选项&#xff1a;】 -L&#xff1a;用于指定递归显示的深度&#xff0c;指定的深度必须是大于0的整数。 -P&#xff1a;用于显示统配符匹配模式的目录和文件&#xff0c;但是…

查看LINUX进程内存占用情况

可以直接使用top命令后&#xff0c;查看%MEM的内容。可以选择按进程查看或者按用户查看&#xff0c;如想查看oracle用户的进程内存使用情况的话可以使用如下的命令&#xff1a; (1)top top命令是Linux下常用的性能分析工具&#xff0c;能够实时显示系统中各个进程的资源占用状况…

ARM(IMX6U)裸机汇编LED驱动实验——驱动编写、编译链接起始地址、烧写bin文件到SD卡中并运行

参考&#xff1a;Linux之ARM&#xff08;IMX6U&#xff09;裸机汇编LED驱动实验–驱动编写 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-08-07 09:13:48 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107851318 参考&#xff1a;Linux之…