Java并发编程的艺术(一)——并发编程需要注意的问题
并发是为了提升程序的执行速度,但并不是多线程一定比单线程高效,而且并发编程容易出错。若要实现正确且高效的并发,就要在开发过程中时刻注意以下三个问题:
- 上下文切换
- 死锁
- 资源限制
接下来会逐一分析这三个问题,并给出相应的解决方案。
问题一:上下文切换会带来额外的开销
线程的运行机制
- 一个CPU每个时刻只能执行一条线程;
- 操作系统给每条线程分配不同长度的时间片;
- 操作系统会从一堆线程中随机选取一条来执行;
- 每条线程用完自己的时间片后,即使任务还没完成,操作系统也会剥夺它的执行权,让另一条线程执行
什么是“上下文切换”?
当一条线程的时间片用完后,操作系统会暂停该线程,并保存该线程相应的信息,然后再随机选择一条新线程去执行,这个过程就称为“线程的上下文切换”。
上下文切换的过程
- 暂停正在执行的线程;
- 保存该线程的相关信息(如:执行到哪一行、程序计算的中间结果等)
- 从就绪队列中随机选一条线程;
- 读取该线程的上下文信息,继续执行
上下文切换是有开销的
每次进行上下文切换时都需要保存当前线程的执行状态,并加载新线程先前的状态。
如果上下文切换频繁,CPU花在上下文切换上的时间占比就会上升,而真正处理任务的时间占比就会下降。
因此,为了提高并发程序的执行效率,让CPU把时间花在刀刃上,我们需要减少上下文切换的次数。
如何减少上下文切换?
减少线程的数量
由于一个CPU每个时刻只能执行一条线程,而傲娇的我们又想让程序并发执行,操作系统只好不断地进行上下文切换来使我们从感官上觉得程序是并发执的行。因此,我们只要减少线程的数量,就能减少上下文切换的次数。
然而如果线程数量已经少于CPU核数,每个CPU执行一条线程,照理来说CPU不需要进行上下文切换了,但事实并非如此。控制同一把锁上的线程数量
如果多条线程共用同一把锁,那么当一条线程获得锁后,其他线程就会被阻塞;当该线程释放锁后,操作系统会从被阻塞的线程中选一条执行,从而又会出现上下文切换。
因此,减少同一把锁上的线程数量也能减少上下文切换的次数。采用无锁并发编程
我们知道,如果减少同一把锁上线程的数量就能减少上下文切换的次数,那么如果不用锁,是否就能避免因竞争锁而产生的上下文切换呢?
答案是肯定的!但你需要根据以下两种情况挑选不同的策略:- 需要并发执行的任务是无状态的:HASH分段
所谓无状态是指并发执行的任务没有共享变量,他们都独立执行。对于这种类型的任务可以按照ID进行HASH分段,每段用一条线程去执行。 - 需要并发执行的任务是有状态的:CAS算法
如果任务需要修改共享变量,那么必须要控制线程的执行顺序,否则会出现安全性问题。你可以给任务加锁,保证任务的原子性与可见性,但这会引起阻塞,从而发生上下文切换;为了避免上下文切换,你可以使用CAS算法,
仅在线程内部需要更新共享变量时使用CAS算法来更新,这种方式不会阻塞线程,并保证更新过程的安全性。
- 需要并发执行的任务是无状态的:HASH分段
问题二:并发不当可能会产生死锁
什么是“死锁”?
当多个线程相互等待已经被对方占用的资源时,就会产生死锁。
死锁示例
class DeadLock {
// 锁A
private Object lockA;
// 锁B
private Object lockB;
// 第一条线程
Thread t1 = new Thread(new Runnable(){
void run () {
synchronized (lockA) {
Thread.sleep(5000);
synchronized (lockB) {
System.out.println("线程1");
}
}
}
}).start();
// 第二条线程
Thread t2 = new Thread(new Runnable(){
void run () {
synchronized (lockB) {
Thread.sleep(5000);
synchronized (lockA) {
System.out.println("线程2");
}
}
}
}).start();
}
- 线程1和线程2都需要锁A和锁B
- 线程1首先获得锁A,然后sleep 5秒
PS:线程sleep过程中会释放执行权 - 此时线程2执行,获得锁B,然后也sleep 5秒;
- 线程1 sleep 5秒后继续执行,此时需要锁B,然而锁B已经被线程2持有,因此线程1被阻塞;
- 此时线程2醒了,它需要锁A,然而锁A已经被线程1持有,因此它也被阻塞;
- 此时死锁出现了!两条线程相互等待已经被占用的资源,程序就死在这了。
死锁是并发编程中一个重要的问题,上面介绍的减少上下文切换只是为了提升程序的性能,而一旦产生死锁,程序就不能正确执行!
如何避免死锁?
- 不要在一条线程中嵌套使用多个锁;
- 不要在一条线程中嵌套占用多个计算机资源;
- 给锁和资源加超时时间
如果你非要在一条线程中嵌套使用多个锁或占用多个资源,那你需要给锁、资源加超时时间,从而避免无限期的等待。
问题三:计算机资源会限制并发
误区:线程越多速度越快
在并发编程中,并不是线程越多越好,有时候线程多了反而会拉低执行效率,原因如下:
- 线程多了会导致上下文切换增多,CPU花在上下文切换的时间增多后,花在处理任务上的时间自然就减少了。
- 计算机资源会限制程序的并发度。
- 比如:你家网入口带宽10M,你写了个多线程下载的软件,同时开100条线程下载,那每条线程平均以每秒100k的速度下载,然而100条线程之间还要不断进行上下文切换,所以你还不如只开5条线程,每条平均2M/s的速度下载。
- 再比如:数据库连接池最多给你用10个连接,然而你却开了100条线程进行数据库操作,那么当10个用完后其他线程就要等待,从而操作系统要在这100条线程间不断进行上下文切换;所以与其这样还不如只开10条线程,减少上下文切换的次数。
说了这么多只想告诉你一个道理:线程并不是越多越好,要根据当前计算机所能提供的资源考虑。
什么是“资源”?
资源分为硬件资源和软件资源:
- 硬件资源
- 硬盘读写速度
- 网络带宽
- 等
- 软件资源
- Socket连接数
- 数据库连接数
- 等
如何解决资源的限制?
- 花钱买更高级的机器
- 根据资源限制并发度
Java并发编程的艺术(一)——并发编程需要注意的问题的更多相关文章
- 《Java并发编程的艺术》并发编程的挑战(一)
并发编程的挑战 并发编程的初衷是让程序运行的更快,但是更多的使用多线程真的会让程序变快吗? 1.线程上下文切换 关于线程上下文切换 多个线程在一个处理器里并不是同时进行的,而是非常快速地在线程之间进行 ...
- 《Java并发编程的艺术》并发编程的基础(四)
一.线程简介 1.线程的概念 系统运行的最小单元 2.为何使用多线程 更好地利用系统资源(处理器多核心),提高响应速度. 3.线程的状态 NEW(创建状态) RUNABLE(运行状态,系统调度,争抢时 ...
- 读《Java并发编程的艺术》(一)
离开博客园很久了,自从找到工作,到现在基本没有再写过博客了.在大学培养起来的写博客的习惯在慢慢的消失殆尽,感觉汗颜.所以现在要开始重新培养起这个习惯,定期写博客不仅是对自己学习知识的一种沉淀,更是在督 ...
- Java并发编程的艺术读书笔记(2)-并发编程模型
title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...
- Java并发编程的艺术读书笔记(1)-并发编程的挑战
title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...
- 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
<Java并发编程实战>和<Java并发编程的艺术> Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...
- Java并发编程的艺术(六)——线程间的通信
多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...
- Java并发编程的艺术(三)——volatile
1. 并发编程的两个关键问题 并发是让多个线程同时执行,若线程之间是独立的,那并发实现起来很简单,各自执行各自的就行:但往往多条线程之间需要共享数据,此时在并发编程过程中就不可避免要考虑两个问题:通信 ...
- java并发编程的艺术(一)---锁的基本属性
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
随机推荐
- LOJ 10160 - 「一本通 5.2 练习 3」周年纪念晚会 / 没有上司的晚会
题面 传送门 Ural 州立大学的校长正在筹备学校的 8080 周年纪念聚会.由于学校的职员有不同的职务级别,可以构成一棵以校长为根的人事关系树.每个资源都有一个唯一的整数编号,从 $1$ 到 $N$ ...
- Java第三阶段学习(八:网络通信协议、UDP与TCP协议)
一.网络通信协议 1.概念: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传 ...
- Servlet接口、GenericServlet类、HttpServlet类
Servlet是最顶层的接口,其提供的方法有: init(ServletConfig config):void // 初始化 getServletConfig():ServletConfig // 取 ...
- Linux学习路线+资源
Linux学习路线,个人收集分享 学习路线图 资源链接(蓝色下划线字体对应相应资源链接) Linux 基础 Linux 基础 Linux安装专题教程 Linux中文环境 Linux—从菜鸟到高手 鸟哥 ...
- [ 转载 ] Python Web 框架:Django、Flask 与 Tornado 的性能对比
本文的数据涉及到我面试时遇到过的问题,大概一次 http 请求到收到响应需要多少时间.这个问题在实际工作中与框架有比较大的关系,因此特别就框架的性能做了一次分析. 这里使用 2016 年 6 月 9 ...
- JFreeChart 之饼图
JFreeChart 之饼图 一.JFreeChart 简介 JFreeChart是JAVA平台上的一个开放的图表绘制类库.它完全使用JAVA语言编写,是为applications, applets, ...
- 8.5 正睿暑期集训营 Day2
目录 2018.8.5 正睿暑期集训营 Day2 总结 A.占领地区(前缀和) B.配对(组合) C 导数卷积(NTT) 考试代码 T1 T2 T3 2018.8.5 正睿暑期集训营 Day2 时间: ...
- Codeforces Round #281 (Div. 2) A. Vasya and Football 模拟
A. Vasya and Football 题目连接: http://codeforces.com/contest/493/problem/A Description Vasya has starte ...
- ROS知识(20)----SLAM资源集合
1.各种最新开源的SLAM a.OpenSLAM.这里收集了各种最新的开源SLAM资料,包含了比如: ORB_SLAM, ORB_SLAM2, hector_slam,ethzasl_ptam,g2o ...
- mysql导入csv文件
今天尝试将Oracle中的数据导入到mysql中,在SQLyog工具其中看到一些sql语句,拿来记录一下,说不定以后就用的着呐! -----查看ydtf数据库中的基础表,就是用户创建了哪些表 SHOW ...