从Java看多核并发编程的2.0趋势

      2.0时代的软件空前地活跃在人类生活的方方面面,从而带来了更多的计算量。所以2.0不可避免地对计算提出了新的需求、引起了新的思考,在这其中,多核带来的并行计算和并发编程无疑是最为深入的一点。

      咱们都能依稀记得,在2005年Sun发布了代号为Tiger的Java 5。在其众多的特性之中, JVM的改进和java.util.concurrent包的出现无疑很是引人注目。改进后的JVM可使用低层机器指令取代锁,精化了互斥访问的粒度,提升了系统的可伸缩性和活性。而concurrent包提供了大量线程和锁之上的并发抽象,好比线程池、闭锁、信号量、关卡等。这些能够帮助开发者快速地构建出高效、可伸缩的系统来。就在同一年,还发生了另外一件影响深远的事件:AMD首次发布了其双核CPU,从而打响了AMD与Intel两大芯片厂商的多核之争。java

当Java遇到多核程序员

     早在十多年前,IBM、Sun与HP就已经设计出了双核处理器,好比IBM于2001年推出的基于双核的POWER4处理器和Sun的UltraSPARC芯片,但这些双核处理器都是用于高端的RISC领域,价格高昂,被大众使用的X86并无享受到它带来的性能优点。直到Intel和AMD相继推出本身的双核处理器后,X86领域才算是有了本身的多核架构。编程

     所谓双核处理器,简单地说就是在一块CPU基板上集成两个处理器核心,并经过并行总线将各处理器核心链接起来。多核并非一个新概念,而只是CMP(Chip Multi Processors,单芯片多处理器)中最基本、最简单、最容易实现的一种类型。其实在RISC处理器领域,多核都早已经实现。安全

多核与单核的区别在于,前者可让程序真正地“同时”执行,而不是多个进程轮流使用CPU,从而给用户形成“多个程序正在同时执行”的假象。简单地说,“并发”就是为了让程序运行得更快。在之前,达到这个目的的手段一般是依赖CPU时钟频率的提高。然而普通单核心处理器的频率难于提高,性能没有质的飞跃。因为频率难于提高,Intel在发布3.8GHz的产品之后只得宣布中止4GHz的产品计划;而AMD在实际频率超过2GHz之后也没法大幅度提高,3GHz成为了AMD没法逾越的一道坎。所以,CPU内部开始出现了两个、四个甚至更多的内核。架构

     为了充分发挥每个核的效用,应用程序须要多个线程同时运行来保持CPU核的忙碌。Java能够帮助你在多核系统上构建良好的应用程序。它能够方便地、相对便宜地建立线程,这很是重要。若是建立线程的开销比线程完成工做的开销还要大,那么并发将变得毫无心义。concurrent包中提供的并发构建块很是丰富,几乎覆盖了全部并发编程中用到的工具。不只如此,Java还在快速地演变着,以适应更高并发性系统的构建,例如,Java 7中即将加入ForkJoin框架,按照Doug Lea的描述,它专门适用于“>32个CPU(内核)的系统”。并发

     除了这些API层面的支持外,Java在并发编程上的底层平台上显得更加野心勃勃,由于Java有本身的存储模型,这个存储模型早在Java 1.1时代就存在了,尽管当时还不完善,但綷-过屡次修补和改进,已綷-变得很是成熟了。Java存储模型能够抹平不一样硬件平台提供的存储模型的差别,严格地定义了线程间通信的规范。例如,对于C语言来讲,相同的代码在X86和PowerPC上会有不一样的语义。框架

     细心的读者可能注意到,Java不过是这场变革的一个突出的表明而已。不少语言都在不一样程度上调整本身,以适应2.0计算时代对并发的须要。在不久,当有人问C#之父Anders Hejlsberg,“将来几年内语言的发展方向在何处”时,Anders表示“要处理好多核的问题,并提供一个更好的并发模型”。Erlang语言最近也受到愈来愈多的关注,这说明人们迫切须要一个强大而又充分简单的工具来解决并发编程的挑战。编程语言

     以Java语言为表明的编程语言在面对并发时代所作出的努力是使人激动的,可是相比于硬件,软件的发展老是滞后的,人们老是等意识到软件出现问题了才开始着手改进。2.0时代的并发计算正在向桌面和客户端转移,可是有多少人作好准备了?因而有人开始惊呼:狼来了?模块化

     关于“狼来了”的讨论,最好的结论就是不管狼是否真的会来,羊圈的篱笆仍然都要修理。这里的“篱笆”就是指并发编程技术。因为是底层的计算平台正在发生变化,所以不只仅是开发者,包括需求分析人员、设计者、程序员和测试者都应该在工做时考虑到并发带来的影响。好比:高并发

     咱们常常要思考,如何才能得到一个最佳的程序粒度,同时保证它们最大限度地彼此隔离,从而能够简单地分配到不一样的处理器单元上?线程间通信的内容有哪些?等等。要想很好地回答这些问题,仅靠程序员或者设计者是不够的。它须要从获取需求开始,就着力对任务进行划分。系统对于并发性的要求,很难在开发的中后期经过“重构”来引入,所以必须在设计之初就给予关注。评价软件有不少标准,好比可扩展性、模块化、松耦合等等,今天还要考虑软件是否有足够的并发性,以充分利用底层的计算资源。它必将成为衡量软件质量的重要标准之一。

     编写并发程序须要面对不少挑战。尤为是多核的流行,它使程序中能够有多个线程真正地 “同时”运行。所以开发者要面对的一个最大挑战是划分任务。也许你须要对数据进行划分,清晰地识别出任务边界,还要尽量地让每一个任务在执行时只使用本身的数据。若是不一样的线程要共享数据,问题将迅速变得复杂。你没法再像之前那样,只要等上几个月,就能够换上更强劲的处理器,从而让你的程序运行的更快。今天,“免费的午饭”已经结束了。能不能把并行化的工做彻底交给操做系统和编译器呢?这是一个充满诱惑力的愿景-可是,并行化的工做如今没法自动实现,将来也只能在必定程度上有所缓解,而不可能所有交由机器完成。并行化过程的重点在于分解程序的任务流和执行流,这最终被归结为数学问题——一个无解的数学问题!面对多核,咱们当然不能中止让机器变得更聪明的努力,可是对于大多数人来讲,更重要的仍是充实本身的知识储备,适应新的思惟方式,就像当年从过程化编程过渡到面向对象编程时所作的那样。

     并发程序难以开发是事实,同时测试与调试更加困难。由于并发错误一般更加隐蔽,它们只有在高负荷和一些特定时序下才会出现,并且难以再现。所以,并发程序的测试须要更多的投入、更好的工具、更精巧的测试策略。一般,并行程序要对须要测试两个要点:性能与安全性。安全性包括程序是否“作了该作的事”、以及是否“没作不应作的事”两个方面。而性能是指是否在规定的时间里作了正确的事。完整的测试计划应该包括:测试计划、单元测试、代码审查和静态分析等。测试计划应该确保后期的测试行为得以贯彻,同时还要衆-调测试过程当中须要的各类资源。并发程序测试的难度远远高于串行程序测试。以单元测试为例,咱们几乎没法只使用惟一的线程完成并发测试,至少须要两个线程。然而在JUnit框架,只能识别其自身的线程,对于其余线程抛出的断言失败或异常,毫无察觉。所以,测试者在编写时就要编写大量衆-调线程的代码,这自己就会带来新的bug,更糟糕的是,不良的测试还会掩盖被测代码中的错误。

并发,释放2.0的力量

     2.0时代是软件空前繁荣的时代,软件将帮助愈来愈多的人完成愈来愈多工做。咱们也一直在探讨解决2.0时代的计算方法。那么,无限度地提高CPU核的时钟频率或者在一个CPU内加入更多的内核,就是2.0时代的计算方法了么?不是。2.0时代的计算方法不只要有强劲的计算硬件作基础,也要有更优秀、更复杂的软件作后盾。单靠硬件的发展是没法知足计算量的激增的,软件也必须作出相应的调整。

     在多核系统上进行并发编程仍然很困难,它不符合人类的思惟方式。纵观计算机软件开发的历史,老是伴随着新技术推出、开发者学习消化的旋律。从最先的几十个机器指令,到今天的结构化语言、面向对象、AOP--

     2.0时代的另外一个效应是,它把并行计算和并发编程摆在了更多的程序员面前。之前,并发编程仍是计算机科学家的专利,不少开发者都将它视为“禁地”。现在,除了复杂的商业应用和庞大的科学计算外,桌面端、各类终端都在逐渐地走向高并发的运行环境。咱们能够学习新的技术、新的思路或者新的语言,好比,在上世纪80年代诞生于爱立信实验室的Erlang语言,近期就表现出极大的活力。在Erlang中,若是要访问共享数据,就须要向数据的拥有者发送一条消息并等待回应,这种方法在构造高可用的并发系统时已綷-取得了极大的成功。尤为是咱们已经习惯了基于对象的抽象。要让并发变得容易,就要放弃一些抽象。在此咱们不去讨论什么是正确的取舍,可是咱们正在苦于这样的幻想:“能不能不作任何取舍呢?”

     相对于Web2.0和企业2.0的波涛汹涌来讲,计算2.0显得有些波澜不惊。可是,这种处于底层的深入变化,将会完全改变上层应用的面貌。多核的普及,将使并行/并发的大行其道,企业或者我的只有快速抓住这个趋势,才能顺利地畅游于软件的2.0时代。(文/韩锴 方妙)

     韩 Works咨询师。从事Eclipse插件与客户端软件开发,对并发程序设计、敏捷过程具备浓厚的兴趣。

相关文章
相关标签/搜索