Java理论与实践: 修复Java内存模子,第2部份[Java编程]
本文“Java理论与实践: 修复Java内存模子,第2部份[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
活泼了将近三年的 JSR 133,近期公布了关于若何修复 Java 内存模子 (Java Memory Model, JMM)的公开倡议.在本系列文章的 第 1 部份,专栏作 者 Brian Goetz 主要介绍最初的 JMM 中的几个严重缺陷,这些缺陷招致了一些 难度高得惊人的概念语义,这些概念本来被认为很简单.这个月,他介绍在新 JMM 中 volatile 和 final 的语义是若何改变的,这些改变使它们的语义符合 大大都开辟人员的直觉.此中一些改变已经在 JDK 1.4 中呈现了,另一些改变 则要等到 JDK 1.5.请您在本文的谈论论坛上与作者及其他读者交流您的设法.
开始编写并发代码是一件艰难的事情,语言不该当增添它的难度.固然 Java 平台从一开始就包含了对线程的支持,包含一个筹划为精确同步的程序供应“一 次编写,处处运行”保证的、跨平台的内存模子,但是本来的内存模子有一些漏 洞.固然很多 Java 平台供应了比 JMM 所要求的更强的保证,但是 JMM 中的漏 洞使得无法简单地编写可以在任何平台上运行的并发 Java 程序.所以在 2001 年 5 月,成立了以修复 Java 内存模子为目的的 JSR 133. 上个月,我谈论了 此中一些漏洞,这个月,我们将谈论若何堵住它们.
修复后的可见性
理解 JMM 所需求的一个关键概念是 可见性(visibility)——若何知道当 线程 A 履行 someVariable?=?3 时,其他线程能否可以看到线程 A 所写的值 3 ?有一些缘由使其他线程不能当即看到 someVariable 的值 3:大概是因为编译 器为了履行效率更高而重新排序了指令,也大概是 someVariable 缓存在存放器 中,大概它的值写到写处理器的缓存中、但是还没有革新到主存中,大概在读处 理器的缓存中有一个老的(大概无效的)值.内存模子决意什么时刻一个线程可 以坚固地“看到”由其他线程对变量的写入.分外是,内存模子定义了保证内存 操作跨线程的可见性的 volatile 、 synchronized 和 final 的语义.
当线程为释放相关监督器而退出一个同步块时,JMM 要求本地处理器缓冲刷 新到主存中.(实际上,内存模子不触及缓存——它触及一个抽象( 本地内存 ), 它包抄了缓存、注册表和其他硬件和编译优化.)与此近似,作为得到监督 的一部份,当进入一个同步块时,本地缓存失效,使之后的读操作直接进入主内 存而不是本地缓存.这一历程保证当变量是由一个线程在由给定监督器保护的同 步块中写入,并由另一个线程在由同一监督器保护的同步块中读取时,对变量的 写可被读线程看到.假如没有同步,则 JMM 不供应这种保证——这就是为什么 在多个线程拜候同一个变量时,必须利用同步(大概它的更年青的同胞 volatile ).
对 volatile 的新保证
volatile 本来的语义只保证 volatile 字段的读写直接在主存而不是存放器 大概本地处理器缓存中举行,并且代表线程对 volatile 变量举行的这些操作是 按线程要求的次序举行的.换句话说,这意味着老的内存模子只保证正在读或写 的变量的可见性,不保证写入其他变量的可见性.固然可以简单实现它,但是它 没有像最初假想的那么有效.
固然对 volatile 变量的读和写不能与对其他 volatile 变量的读和写一同 重新排序,但是它们仍旧可以与对 nonvolatile 变量的读写一同重新排序.在 第 1 部份 中,介绍了清单 1 的代码(在旧的内存模子中)是若何不足以保证 线程 B 看到 configOptions 及通过 configOptions 间接可及的全部变量(如 Map 元素)的精确值,因为 configOptions 的初始化大概已经随 volatile initialized 变量举行重新排序.
清单 1. 用一个 volatile 变量作为“保护”
Map configOptions;
char[] configText;
volatile boolean initialized = false;
// In Thread A
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
// In Thread B
while (!initialized)
sleep();
// use configOptions
不幸地,这是 volatile 常见用例——用一个 volatile 字段作为“保护” 表明已经初始化了一组同享变量.JSR 133 Expert Group 决意让 volatile 读 写不能与其他内存操作一同重新排序是有意义的——可以精确地支持这种和其他 近似的用例.在新的内存模子下,假如当线程 A 写入 volatile 变量 V 而线程 B 读取 V 时,那么在写入 V 时,A 可见的全部变量值目前都可以保证对 B 是 可见的.后果就是作用更大的 volatile 语义,代价是拜候 volatile 字段时会 对性能产生更大的影响.
以上是“Java理论与实践: 修复Java内存模子,第2部份[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |