Java理论与实践: 修复Java内存模子,第1部份[Java编程]
本文“Java理论与实践: 修复Java内存模子,第1部份[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
活泼了将近三年的 JSR 133,近期公布了关于若何修复 Java 内存模子 (Java Memory Model, JMM)的公开倡议.原始 JMM 中有几个严重缺陷,这导 致了一些难度高得惊人的概念语义,这些概念本来被认为很简单,如 volatile 、final 以及 synchronized.在这一期的 Java 理论与实践 中,Brian Goetz 展示了若何加强 volatile 和 final 的语义,以修复 JMM.这些更改有些已经 集成在 JDK 1.4 中;而另一些将会包含在 JDK 1.5 中.
Java 平台把线程和多处理技术集成到了语言中,这种集成程度比从前的大多 数编程语言都要强很多.该语言关于平台独立的并发及多线程技术的支持是野心 勃勃并且是具有开辟性的,大概并不奇特,这个问题要比 Java 体系构造计划者 的原始假想要略微艰难些.关于同步和线程安全的很多底层混合是 Java 内存模 型 (JMM)的一些难以直觉到的渺小差别,这些差别最初是在 Java Language Specification 的第 17 章中指定的,并且由 JSR 133 重新指定.
比方,并非全部的多处理器系统都表现出 缓存一致性(cache coherency );假定有一个处理器有一个更新了的变量值位于其缓存中,但还没有被存入主 存,这样别的处理器便大概会看不到这个更新的值.在缓存贫乏一致性的情形下 ,两个差别的处理器可以看到在内存中同一位置处有两种差别的值.这听起来不 太大概,但是这倒是成心的 —— 这是一种得到较高的性能和可伸缩性的办法 —— 但是这加重了开辟者和编译器为办理这些问题而编写代码的负担.
什么是内存模子,我为什么需求一个内存模子?
内存模子描写的是程序中各变量(实例域、静态域和数组元素)之间的关系 ,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节 .对象终究存储在内存中,但编译器、运行库、处理器或缓存可以有特权按时地 在变量的指定内存位置存入或取出变量值.比方,编译器为了优化一个循环索引 变量,大概会挑选把它存储到一个存放器中,大概缓存会耽误到一个更合适的时 间,才把一个新的变量值存入主存.全部的这些优化是为了帮忙实现更高的性能 ,普通这关于用户来说是透明的,但是对多处理系统来说,这些复杂的事情大概 有时会完好显现出来.
JMM 答应编译器和缓存以数据在处理器特定的缓存(或存放器)和主存之间 移动的次序拥有重要的特权,除非程序员已经利用 synchronized 或 final 明 确地恳求了某些可见性保证.这意味着在贫乏同步的情形下,从差别的线程角度 来看,内存的操作是以差别的次序发生的.
与之相对应地,像 C 和 C++ 这些语言就没有显示的内存模子 —— 但 C 语 言程序担当了执路程序处理器的内存模子(固然一个给定体系构造的编译器大概 知道有关底层处理器的内存模子的一些情形,并且保持一致性的一部份责任也落 到了该编译器的头上).这意味着并发的 C 语言程序可以在一个,而不能在另 一个,处理器体系构造上精确地运行.固然一开始 JMM 会有些混乱,但这有个 很大的好处 —— 按照 JMM 而被精确同步的程序能精确地运行在任何支持 Java 的平台上.
原始 JMM 的缺陷
固然在 Java Language Specification 的第 17 章指定的 JMM 是一个野心 勃勃的尝试,它尝试定义一个一致的、跨平台的内存模子,但它有一些渺小而重 要的缺陷. synchronized 和 volatile 的语义很让人混合,乃至于很多有见解 的开辟者有时挑选忽视这些法则,因为在旧的存储模子下编写精确同步的代码非 常艰难.
旧的 JMM 答应一些奇特而混乱的事情发生,比方 final 字段看起来没有那 种设置在构造函数里的值(这样使得想像上的不可变对象并非不可变的)和内 存操作重新排序的不测后果.这也避免了其他一些有效的编译器优化.假如您阅 读了关于双重查抄锁定问题(double-checked locking problem)的任何文章( 参阅 参考资料),您将会记得内存操作重新排序是多么的混乱,以及当您没有 精确地同步(大概没有主动地试图避免同步)时,渺小却严重的问题会若何躲藏 在您的代码中.更糟糕的是,很多没有精确同步的程序在某些情形下仿佛工作得 很好,比方在轻细的负载下、在单处理器系统上,大概在具有比 JMM 所要求的 更强的内存模子的处理器上.
“重新排序”这个术语用于描写几种对内存操作的真实明显的重新排序的类 型:
当编译器不会改变程序的语义时,作为一种优化它可以随便地重新排序某些 指令.
在某些情形下,可以答应处理器以倒置的次序履行一些操作.
普通答应缓存以与程序写入变量时所不相同的次序把变量存入主存.
从另一线程的角度来看,任何这些条件城市引发一些操作以差别于程序指定 的次序发生 —— 并且忽视重新排序的源代码时,内存模子认为全部这些条件都 是平等的.
JSR 133 的目标
JSR 133 被受权来修复 JMM,它有几个目标:
保存现有的安全保证,包含范例安全.
供应 无中生有安全性(out-of-thin-air safety).这意味着变量值并非 “无中生有”地成立的 —— 所以关于一个线程来说,要察看到一个变量具有变 量值 X,必须有某个线程从前已经真正把变量值 X 写入了那个变量.
“精确同步的”程序的语义应当尽大概简单直观.这样,“精确同步的”应 该被正式而直观地定义(这两种定义应当彼此一致).
程序员应当要有信心成立多线程程序.当然,我们没有魔法使得编写并发程 序变得很简单,但是我们的目标是为了减轻程序员理解内存模子全部细节的负担 .
跨大范围的风行硬件体系构造上的高性能 JVM 实现应当是大概的.现代的处 理器在它们的内存模子上有着很大的差别;JMM 应当可以合适于实际的尽大概多 的体系构造,而不会以牺牲性能为代价.
供应一个同步习惯用法(idiom),以答应我们公布一个对象并且使得它不用 同步便可见.这是一种叫做 初始化安全(initialization safety)的新的安全 保证.
对现有代码应当只有最小限度的影响.
值得注意的是,有漏洞的技术(如双重查抄锁定)在新的内存模子下仍旧有 漏洞,并且“修复”双重查抄锁定技术并非新内存模子所努力的一个目标.( 但是, volatile 的新语义答应普通所提出的此中一个双重查抄锁定的可选办法 精确地工作,固然我们不鼓舞这种技术.)
从 JSR 133 process 变得活泼的三年来,人们发现这些问题比他们认为重要 的任何问题都要奇妙得多.这就是作为一个开辟者的代价!终究正式的语义比原 来所意料的要复杂得多,实际上它采取了一种与原先预想的完好差别的情势,但 非正式的语义是清楚直观的,将在本文的第 2 部份概要地阐明.
以上是“Java理论与实践: 修复Java内存模子,第1部份[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |