在多核或多处理器时代,为什么要特别 重视 Java 多线程 开发模式

在多核或多处理器时代,为什么要特别 重视 Java 多线程 开发模式
        就在不久前,改善程序的性能还很容易。你可以用性能分析工具看看程序在做什么,研究一下代码的内层循环;也可以编写几十个测试用例,变换程序的负载,看看何种优化在何种情形下会有用;还可以手工打造每次调用调用都能节省几个时钟周期的轻巧数据结构。如果你既刻苦,又幸运,所有的这些工作会花费你18个月的时间,到时候新一代的处理器又出来了,突然之间你的程序比原来快了两倍左右。就像涂肥皂,冲水,再来一次……这样一个反复的过程。
        最近这种模式改变了,加快处理器的时钟周期(从而提高它的速度)变得越来越困难了。人们现在开始担心能效,它会随着时钟的加快而降低。能源问题的直接结果是发热问题,随着芯片的速度越来越快,散热也越来越困难。很久都没有看到CPU的原始速度有重大提升了。
       我们任然看到摩尔定律在起作用,CPU的设计者们继续在往每片硅晶上塞入越来越多的晶体管。但他们不再使用这新晶体管加快CPU的速度,而是用于生产多核的芯片,让多个CPU共享一个芯片。多核处理器的思想是:若果你有多个程序运行在一台机器上,你可以让每个程序单独占据一个核,从而得到多倍的整体性能。
         如今,不再是每隔18个月芯片的速度提高一倍,而似乎是每隔18个月芯片里的核多出一倍。芯片操作工仍可声称他们每隔18个月便提高一倍的性能,但现在他们指的是:可同时运行的程序的数目多出一倍。
        这些额外的核对自动加快程序的速度没有任何贡献程序运行的时间将与它们在上一代硬件上的运行时间相同,除非,你编写程序时刻意利用这些额外的核如果你的程序有高度的并发性,从而能同时运行在多个核上,把运行程序的核增加一倍就可让程序的速度提高一倍。但你必须以一种不同的方式来写程序,而不再像编写标准的顺序程序那样,你需要让你自己的程序主动利用并发性。
        并发编程必然有难度,通常留给底层系统的编程高手来完成,他们向普通程序员提供一种顺序化视角来看待世界。然而,Java从一开始就打算用于并发系统中,在这种系统中,高度的并发性是获取可接受性能的唯一办法。因而,它有着可用于构建可靠并发程序的语言设施(如用于开发并发程序的api),正确的使用这些设施,你就能充分利用芯片设计的新进展。即使你不需要亲自编写高度并发的代码,但如果你设计一个类是为了复用它,那你也应该努力把这些类写好,使它们可以再高度并发的代码中被安全的使用,这需要你理解java的并发设施。Java拥有这种设施是一件好事,对当前硬件发展的趋势来说更是如此,若要正确使用这些设施,需要信心和慎重的思考。

        最起码,你应该了解正确处理并发有多难,这会给出更多的理由让你不去编写自己的多线程类,相反,只有有机会,就应使用别人已经写好且调试好并优化过的类。Java集合有一套并发安全的实现,程序员不必自己动手编写,可直接使用它们,但还要保证自己读过这些类的文档,弄清楚它们在多线程环境中是否安全,而且保证自己不会把不安全的类当成安全的来用。最后,对于自己实现的类能否安全地用在多线程代码中,要确保你的文档中有明确的说明。

      Java开发中如何使用多线程
       我们已经见过一种类似使用多线程的方式。如果你能把一个较大的程序分解成一组相互独立的更小的程序,它们之间通过远程方法调用( RMI)来通信,你就可以同时运行不同的程序,从而利用多核。这里假定你这么做之后真正获得了并行效果,而不是让不同的Java虚拟机运行在单个硬件线程中,不过有时候那样做也完全可以。其实上述所述算不上真正的多线程,而是一种基于RMI的分布式的系统开发模式。(注:与计算机科学中的其它术语一样,线程“Thread”在不同的上下文中可以指代不同的内如。我们有硬件线程,软件线程,以及二者之间的复杂关系)
        一种更强大的方法是使用多线程在单台Java虚拟机中获得并发性。线程与进程不同,线程之间可以共享同一地址空间,因此,它们可以通过访问同一段内存的方式来沟通或协调,这使得线程间的通信可以比使用RMI快的多。但这也意味着两个线程可能在同一时间针对同一内存位置做不同的事情,并相互干扰。并发编程的诀窍就在于设置正确的保护机制,确保多线程共享的内存不会引发新的,更难以捕捉的问题。
       我们已经见过不少适合多线程同时运行的情景。比如,你听的最多的情形是大量的工作分解成并行的计算,然后把每种计算放到单独的线程中。再比如下述情形也适合用多线程的方法来解决:如类中的某个函数调用需要(相对)较长的时间完成。如果我们能调用这些函数,在它完成的之前又做其它事,其他事做完之后再回来获得它的计算结果,那就太好了。
        在分布式程序设计中会有这种情况:跨网络的调用需要花很长时间(对计算机来说),如果能在调用执行的过程中找到其它事情来做,我们就能更合理地利用计算机资源。但使用远程过程调用机制便意味着跨网络的调用看起来就像一次本地方法的调用,而这又意味着我们需要等待调用结束才能做其它事情(Java中,RPC机制是通过RMI标准实现的)。曾有人争论:声称这显示了RPC形式的一个根本弱点,而且这种构建分布式系统的方法应该被舍弃,改用某种面向消息的系统。笔者不想在此深入讨论这个问题,只想说使用并发编程技术,因为这样的争论是说不通的。至少在程序性能的影响方面是说不通的。
        等待RPC调用会消耗大量的性能,除非我们在单独的线程中调用它们。如果我们创建一个新的线程来做RMI调用,就可以让那个线程等待结果,同另一个线程做其它事情。这种方法即使在某一个时刻只能运行一个线程的单核处理器上也是有用的。在RMI调用挂起的时候,另一个线程可被调度执行。
……
        并发编程时语言和软件工程的下一个前言。由于多核系统的出现,我们中的某些一直无视并发编程的人现在不得不选择它,尤其是在大型服务器端程序开发时,为了提高性能,必须采用多线程模式编程。我们必须理解并发,而且随着时间的推移,我们肯定会发明出新的用法和实践,使编写并发系统更加容易,也更程式化。然而在那之前,能有一种可信赖的语言和环境------只要你写出正确的程序,它就能做正确的事情是非常不错的。或许Java给不出多线程的终极方案,但它还是给出了一种方案,可用来发挥新一代芯片的潜力,这本身就足以是并发编程成为Java的精良部分之一。

本文参考:《Java语言精粹》电子工业出版社

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值