MyException - 我的异常网
当前位置:我的异常网» 软件架构设计 » 关于并发的思忖

关于并发的思忖

www.MyException.Cn  网友分享于:2015-02-11  浏览:0次
关于并发的思考
并发数
并发数和2个因素有关,
一是系统可用的处理器核数,这个值可以通过硬件查询得到,也可以通过如下代码得到:
Runtime.getRuntime().availableProcessors();

二是并发任务的类型,任务类型一般分为IO密集型和CPU密集型
  • I/O 密集的任务通常行为是反复去读写磁盘文件,执行任务时,观察 CPU 占用的话多数时间都是出于 I/O wait 状态。这种情况下,当一个任务阻塞在IO操作上时,我们可以立即切换执行其他任务或启动其他IO操作请求,这样并发就可以帮助我们有效地提升程序执行效率。
  • CPU 密集则是大量 CPU 时间都用于进行计算。这种情况下,可以将问题拆分为子任务、并发执行各子任务并最终将子任务的结果汇总合并以提升效率。

说是有这么分,但是90%以上的的任务都是既有I/O操作,又有CPU运算的,因此我们在设定并发任务的时候,更重要的是去关注线程等待时间和线程CPU时间,这2个时间可以通过JMX从线程的ThreadInfo中获取,具体如何做的方式:http://stackoverflow.com/questions/1680865/how-can-i-monitor-cpu-usage-per-thread-of-a-java-application-in-a-linux-multipro
有了上面的2个数据,可以大致估算一个并发的线程数:
引用
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU核数

这只是一个大致估算,可以作为一个起点,在实际生产环境中,我们需要结合系统真实情况(比如是IO密集型或者是CPU密集型或者是纯内存操作)和硬件环境(CPU、内存、硬盘读写速度、网络状况等)来不断调整,寻找到系统的一个最佳性能点。

并发的执行策略
要并发执行任务,首先要做的就是拆分任务,把一个大的过程或者任务拆分成很多小的工作单元,拆分后的每一个工作单元可能相关、也可能无关,但他们在一定程度上可以充分利用CPU的特性并发的执行,从而提高并发性(性能、响应时间、吞吐量等)。

在拆分任务时,尽量把任务拆分得独立,不相互影响,但是在实际的业务场景中,工作单元之间或多或少会存在着一定的依赖关系,对于有依赖关系以及资源竞争的工作单元就涉及到任务的调度和负载均衡。另外,在并发量超过资源承载能力的情况下,也涉及各个工作单元的调度。这就涉及到并发任务的执行策略问题。

一个并发的执行策略主要包含下面几个部分:
  • 任务在如何执行
  • 任务以什么顺序执行(FIFO/LIFO/优先级等)
  • 同时有多少个任务并发执行
  • 允许有多少个个任务进入执行队列
  • 系统过载时选择放弃哪一个任务,如何通知应用程序这个动作
  • 任务执行的开始、结束应该做什么处理

有了思路,来看一下Java世界是怎么实现的?

任务如何执行?在Java的世界,所有的并发任务都必须放到线程Thread中执行,不管你是Runnable/Callable/TimerTask中的哪个,最终执行任务的就是Thread类,因此拆分任务就是形成一个个的小的Thread执行的任务。使用Callable可以获取到任务执行的结果,另外还有Future类可以帮助阻塞当前线程直到异步任务返回结果。

任务以何种顺序执行?在Java中控制并发任务的顺序有两部分,一是线程的优先级,但是这个不能保证一定高优先级的比低优先级的先,只是获得CPU时间片的概率会更高一点,二是,任务等待队列的顺序,即通过有序的分发任务来保证线程的执行顺序。

同时有多少个任务并发执行? 这个通过线程池大小来控制,开发者可以通过上一小节中的并发数估算值来设置线程池大小。而JDK1.5的并发包中提供了辅助类Executors,该类可以非常方便的设置线程池大小,同时解决了向线程池提交任务的入口问题。顺道说一句,其还提供了ScheduledExecutorService来解决重复调用任务的问题。

多少个个任务进入执行队列?这个值可以通过队列的长度来控制,JDK包中提供了各种BlockingQueue的实现,包括有容量限制的ArrayBlockingQueue, 可自定义顺序的PriorityBlockingQueue等等。

系统过载时选择放弃哪一个任务,如何通知应用程序这个动作?Java提供了RejectedExecutionHandler来解决此问题。JDK默认提供了四种方式处理。 DiscardPolicy:直接丢弃当前将要加入队列的任务本身; DiscardOldestPolicy:丢弃任务队列中最旧任务; AbortPolicy:抛出异常RejectedExecutionException; CallerRunsPolicy:调用者线程执行

任务执行的开始、结束应该做什么处理? JDK中首先提供了CompletionService/ExecutorCompletionService类来整合有返回值的Callable任务的所有结果,另外,还可以定制ThreadPoolExecutor的beforeExecute方法和afterExecute()方法。

综上所述,要设置一个合适的并发执行策略是很复杂的,所幸的是Doug Lea大爷在JDK1.5中为大家带来的并发包,提供了大量的辅助类帮助开发者简化这些配置,感恩。

并发模型
对于并发模型, 主要是2派观点,一派是基于内存共享的Thread模型,一派是基于消息传递的Actor模型(Event-Driven模型)。这两者的主要特征如下:
EventsThreads
event handlersmonitors
events accepted by a handlerfunctions exported by a module
SendMessage/AwaitReplyprocedure call,or fork/join
SendReplyreturn from procedure
waiting for messageswaiting on condition variables
 

这两派的观点争执有好几十年了,这边Standford大学1995年讲述了《为什么Events优于Thread?》(http://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf), 主要观点如下:

引用
现在我们有点滥用Thread,Thread是基于共享内存做同步的,同时线程池的执行顺序都是预先设定好的,这就会导致死锁,出了问题很难调试,编程模型被破坏,无法使用回调等问题,而Event-Driven的编程模型,因为没有共享状态,就不需要担心死锁,同步的问题, 而且因为每个Event Handler都有自己处理域的所有信息,因此bug的调试变得简单,另外,无锁也使得在单个CPU上Event模型的性能高于Thread模型。另外,作者也承认Thread模型也有优于Event模型的地方,比方说在多个CPU上的并行能力,另外,Event模型中当某个Event Handler耗时过长时可能导致系统假死等。最终作者的结论是我们应该首先考虑使用Event模型,在一些性能要求特别苛刻的场景下,才考虑使用高端的Thread技巧,去把多核的性能发挥到极致。

那边Berkeley大学在2003年发表了论文《Why Events Are A Bad Idea (for high-concurrency servers)》:http://wenku.baidu.com/link?url=UmFNZN29i3XsMkUfzSUwflOH1Y_qPNbpz4H5zloshCJOC61RfKCgm1P_2iNZtdAKvTMCWsLcHnObfT-950nTlku7GZmuEURgQJXYvTXUELG 观点如下:

引用
其实事件驱动并没有在功能上有比线程有什么优越之处,但编程要麻烦很多,而且特别容易出错。线程的问题,无非是目前的实现的原因。一个是线程占的资源太大,一创建就分配几个MB的stack,一般的机器能支持的线程大受限制。针对这点,可以用自动扩展的stack,创建的先少分点,然后动态增加。第二个是线程的切换负担太大,Linux中实际上process和thread是一回事,区别就在于是否共享地址空间。解决这个问题的办法是用轻量级的线程实现,通过合作式的办法来实现共享系统的线程。这样一个是切换的花费很少,另外一个可以维护比较小的stack。他们用coroutine和nonblocking I/O(用的是poll()+thread pool)实现了一个原型系统,证明了性能并不比事件驱动差。

接着Berkeley大学在2006年自己打脸又发表了论文《The Problem With Thread》(http://wenku.baidu.com/link?url=eCpQcSFqxxIqaRfsoeDcycIVJI0Ff5hqFtU3OWquldRg5ahQx-jQmh2aKj4-H91EP8dhCCErOiKMGTqsuYznVhNDevimi7e2WorofDBRVcm), 表述的观点如下:

引用
目前,程序的模型基本上是基于顺序执行。顺序执行是确定性的,容易保证正确性。而人的思维方式也往往是单线程的。线程的模式是强行在单线程,顺序执行的基础上加入了并发和不确定性。这样程序的正确性就很难保证。线程之间的同步是通过共享内存来实现的,你很难来对并发线程和共享内存来建立数学模型,其中有很大的不确定性,而不确定性是编程的巨大敌人。作者以他们的一个项目中的经验来说明,保证多线程的程序的正确性,几乎是不可能的事情。首先,很多很简单的模式,在多线程的情况下,要保证正确性,需要注意很多非常微妙的细节,否则就会导致deadlock或者race condition。其次,由于人的思维的限制,即使你采取各种消除不确定的办法,比如monitor,transactional memory,还有promise/future,等等机制,还是很难保证面面俱到。以作者的项目为例,他们有计算机科学的专家,有最聪明的研究生,采用了整套软件工程的流程:design review, code review, regression tests, automated code coverage metrics,认为已经消除了大多数问题,不过还是在系统运行4年以后,出现了一个deadlock。作者说,很多多线程的程序实际上存在并发错误,只不过由于硬件的并行度不够,往往不显示出来。随着硬件的并行度越来越高,很多原来运行完好的程序,很可能会发生问题。我自己的体会也是,程序NPE,core dump都不怕,最怕的就是race condition和deadlock,因为这些都是不确定的(non-deterministic),往往很难重现。

对于这两种模型,个人觉得见仁见智了,条条大路通罗马,关键是你通过对自己的问题分析觉得它适合于哪一种模型。
1 楼 Wuaner 22 小时前  
不错,支持。

文章评论

2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
Java程序员必看电影
Java程序员必看电影
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
那些争议最大的编程观点
那些争议最大的编程观点
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
如何成为一名黑客
如何成为一名黑客
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
总结2014中国互联网十大段子
总结2014中国互联网十大段子
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
代码女神横空出世
代码女神横空出世
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
鲜为人知的编程真相
鲜为人知的编程真相
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
 程序员的样子
程序员的样子
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
程序员的鄙视链
程序员的鄙视链
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
10个调试和排错的小建议
10个调试和排错的小建议
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
我的丈夫是个程序员
我的丈夫是个程序员
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
中美印日四国程序员比较
中美印日四国程序员比较
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
程序员都该阅读的书
程序员都该阅读的书
程序员应该关注的一些事儿
程序员应该关注的一些事儿
为什么程序员都是夜猫子
为什么程序员都是夜猫子
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员必看的十大电影
程序员必看的十大电影
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
我是如何打败拖延症的
我是如何打败拖延症的
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
漫画:程序员的工作
漫画:程序员的工作
老程序员的下场
老程序员的下场
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有