20世纪的语言不一定能充分发挥21世纪的硬件的作用。我们之前讨论的内容已经多次暗示了这一点。在第6章讨论晶体管数量增长的摩尔定律时(6.3.1节),由于一个非常重要的原因,我们当时仅简要讨论了一下。那就是摩尔定律、性能和并发之间的相互作用,这也是我们的第一个主题。
14.3.1 多核的世界
尽管晶体管数量的爆炸性增长跟预测一样,但内存的访问速度却没能跟上。在20世纪90年代和21世纪头几年,芯片设计者用大量晶体管解决相对较慢的主存问题。
就像第6章讨论的,这可以确保有稳定的数据流供核心处理。但这根本是一场输掉的战斗:在晶体管上提升的速度变得越来越边际化。这是因为过去用的技术(比如指令级并行和投机式执行)现在已经把容易提升的速度榨光了,投机性变得越来越强。
最近这些年,业界已经把注意力转到了用晶体管在每个芯片上提供更多处理器内核。现在几乎所有笔记本或台式机都至少是双核的,4核和8核也很常见。在更高端的服务器上,可以找到6核或8核的芯片,整机能达到32(或更多)个核心。多核世界就在这里,要充分发挥它的作用,需要以串行处理更少的风格编程。那需要得到语言和运行时环境的支持。
14.3.2 运行时管理的并发
我们已经看到了未来并发编程的开始。在Scala和Clojure中,我们讨论了与Java的线程和锁模型有很大差异的并发观点:Scala的actor模型和Clojure的软件事务型内存方式。
Scala的actor模型允许在运行的代码块之间发送消息,而这些代码可能运行在完全不同的核心上(甚至有允许actor远程运行的扩展)。这就是说代码完全是按以actor为中心的方式编写的,因此在多核机器上扩展非常简单。
跟Scala actor一样,Clojure中的代理填补了相同的生态位1,但Clojure中还有只能在一个内存事务中修改的共享数据(refs)——软件事务型内存机制。
1 生态位(Ecological niche),又称小生境、生态区位、生态栖位或是生态龛位,是一个物种所处的环境以及其本身生活习性的总称。每个物种都有自己独特的生态位,区别于其他物种。——译者注
在这两种并发中,都能见到一种新概念的萌芽:由运行时(而不是开发人员)管理并发。尽管JVM提供了线程调度的底层服务,但它没有提供管理并发程序的高层结构。
这个缺陷在Java语言中能看出来,导致Java程序员基本上在用JVM的底层模型。
别对Java并发太过苛求
Java在1996年发布,它是从一开始就考虑并发的主流语言之一。经过业界15年的广泛实操,我们才对可变数据、默认共享状态和用协作锁强制排他执行这种模型的问题有所察觉。但发布Java 1.0的工程师没有这种福利。从很多方面来说,Java在并发上的首次尝试都是我们取得今天这种成就的基础。
现在有大把的代码撒在外面,再为Java做一种全新的机制来强制推行,还要跟现有代码无缝交互,这非常困难。所以大部分注意力都放在了为非Java的JVM语言找寻新的并发出路上。这些语言有两个重要特性:
- 以JMM为底层模型;
- 跟Java相比有“全新设计”的语言运行时,可以提供不同的抽象层(并且强制性更强)。
在VM层面出现更多的并发支持也不是不可能(下一节会讨论到),但目前来看主流还是在以JMM为基础的新语言上做创新,而不是修改底层的基础线程模型。
在JDK 8及以后的版本中,肯定能见到JVM的某些区域会发生变化。其中的一些变化顺延了Java 7的invokedynamic
,这也是我们要讨论的下一主题。