Java内存模型FAQ(八)Final字段如何改变它们的值

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第八章

译者:Alex

我们可以通过分析String类的实现具体细节来展示一个final变量是如何可以改变的。

String对象包含了三个字段:一个character数组,一个数组的offset和一个length。实现String类的基本原理为:它不仅仅拥有character数组,而且为了避免多余的对象分配和拷贝,多个String和StringBuffer对象都会共享相同的character数组。因此,String.substring()方法能够通过改变length和offset,而共享原始的character数组来创建一个新的String。对一个String来说,这些字段都是final型的字段。

String s1 = "/usr/tmp";
String s2 = s1.substring(4); 

字符串s2的offset的值为4,length的值为4。但是,在旧的内存模型下,对其他线程来说,看到offset拥有默认的值0是可能的,而且,稍后一点时间会看到正确的值4,好像字符串的值从“/usr”变成了“/tmp”一样。

旧的Java内存模型允许这些行为,部分JVM已经展现出这样的行为了。在新的Java内存模型里面,这些是非法的。

原文

How can final fields appear to change their values?

One of the best examples of how final fields’ values can be seen to change involves one particular implementation of the String class.

A String can be implemented as an object with three fields — a character array, an offset into that array, and a length. The rationale for implementing String this way, instead of having only the character array, is that it lets multiple String and StringBufferobjects share the same character array and avoid additional object allocation and copying. So, for example, the method String.substring() can be implemented by creating a new string which shares the same character array with the original String and merely differs in the length and offset fields. For a String, these fields are all final fields.

String s1 = "/usr/tmp";
String s2 = s1.substring(4); 

The string s2 will have an offset of 4 and a length of 4. But, under the old model, it was possible for another thread to see the offset as having the default value of 0, and then later see the correct value of 4, it will appear as if the string “/usr” changes to “/tmp”.

The original Java Memory Model allowed this behavior; several JVMs have exhibited this behavior. The new Java Memory Model makes this illegal.

时间: 2024-11-18 03:26:28

Java内存模型FAQ(八)Final字段如何改变它们的值的相关文章

Java内存模型FAQ(九)在新的Java内存模型中,final字段是如何工作的

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第九章 译者:Alex 一个对象的final字段值是在它的构造方法里面设置的.假设对象被正确的构造了,一旦对象被构造,在构造方法里面设置给final字段的的值在没有同步的情况下对所有其他的线程都会可见.另外,引用这些final字段的对象或数组都将会看到final字段的最新值. 对一个对象来说,被正确的构造是什么意思呢?简单来说,它意味着这个正在构造的对象的引用在构造期

Java内存模型FAQ(三)JSR133是什么?

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第三章 译者:Alex 从1997年以来,人们不断发现Java语言规范的17章定义的Java内存模型中的一些严重的缺陷.这些缺陷会导致一些使人迷惑的行为(例如final字段会被观察到值的改变)和破坏编译器常见的优化能力. Java内存模型是一个雄心勃勃的计划,它是编程语言规范第一次尝试合并一个能够在各种处理器架构中为并发提供一致语义的内存模型.不过,定义一个既一致又直

Java内存模型FAQ(十一)新的内存模型是否修复了双重锁检查问题?

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl 译者:Alex 臭名昭著的双重锁检查(也叫多线程单例模式)是一个骗人的把戏,它用来支持lazy初始化,同时避免过度使用同步.在非常早的JVM中,同步非常慢,开发人员非常希望删掉它.双重锁检查代码如下: // double-checked-locking - don't do this! private static Something instance = n

Java内存模型FAQ(七)同步会干些什么呢

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第七章 译者:Alex 同步有几个方面的作用.最广为人知的就是互斥 --一次只有一个线程能够获得一个监视器,因此,在一个监视器上面同步意味着一旦一个线程进入到监视器保护的同步块中,其他的线程都不能进入到同一个监视器保护的块中间,除非第一个线程退出了同步块. 但是同步的含义比互斥更广.同步保证了一个线程在同步块之前或者在同步块中的一个内存写入操作以可预知的方式对其他有相

Java内存模型FAQ(十)volatile是干什么用的

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile 译者:Alex Volatile字段是用于线程间通讯的特殊字段.每次读volatile字段都会看到其它线程写入该字段的最新值:实际上,程序员之所以要定义volatile字段是因为在某些情况下由于缓存和重排序所看到的陈旧的变量值是不可接受的.编译器和运行时禁止在寄存器里面分配它们.它们还必须保证,在它们写好之后,它们被从缓冲区刷新到主存中,因此,它们立

Java内存模型FAQ(四)重排序意味着什么?

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第四章 译者:Alex 在很多情况下,访问一个程序变量(对象实例字段,类静态字段和数组元素)可能会使用不同的顺序执行,而不是程序语义所指定的顺序执行.编译器能够自由的以优化的名义去改变指令顺序.在特定的环境下,处理器可能会次序颠倒的执行指令.数据可能在寄存器,处理器缓冲区和主内存中以不同的次序移动,而不是按照程序指定的顺序. 例如,如果一个线程写入值到字段a,然后写入

深入理解Java内存模型(六)——final

与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序. 下面,我们通过一些示例性的代码来分别说明这两个规则: public class FinalExample { int i; //普通变量 final

深入理解Java内存模型(六) final

与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译 器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操 作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序. 下面,我们通过一些示例性的代码来分别说明这两个规则: public class FinalExample { int i; //普通变量 fin

Java理论与实践: 修复Java内存模型,第2部分

活跃了将近三年的 JSR 133,近期发布了关于如何修复 Java 内存模型 (Java Memory Model, JMM)的公开建议.在本系列文章的 第 1 部分,专栏作 者 Brian Goetz 主要介绍最初的 JMM 中的几个严重缺陷,这些缺陷导致了一些 难度高得惊人的概念语义,这些概念原来被认为很简单.这个月,他介绍在新 JMM 中 volatile 和 final 的语义是如何变化的,这些改变使它们的语义符合 大多数开发人员的直觉.其中一些改变已经在 JDK 1.4 中出现了,另一