用Final定义的对象为null或未定义的作用?

Java关键字 final,真的有这么神奇的作用嘛? - ITeye问答
这是 JSR-133 关于 final 的说明。其中,第一个例子 FinalFieldExample 如下:
class FinalFieldExample {
static FinalFieldE
public FinalFieldExample() {
static void writer() {
f = new FinalFieldExample();
static void reader() {
if (f != null) {
int i = f.x;
int j = f.y;
然后,作者解释说
1、执行reader的线程保证可以看到f.x为3,因为x定义为final;
2、不保证f.y为4,因为f.y未定义为 final
但是,真的是这样吗?为什么 f.y有可能不为4能,难道就是因为 “调用writer()的线程 thread-locally cache了 y 的值,从而导致其他线程看不到y的值”
我写个测试程序,无论如何也无法得到f.y不为4的情况
public class FinalFieldExample {
static FinalFieldE
public FinalFieldExample() {
static void writer() {
f = new FinalFieldExample();
static void reader() {
while (f != null) {
int i = f.x;
int j = f.y;
if (j != 4) {
System.out.println("j should be 4");
if (i != 3) {
System.out.println("i should be 3");
public static void main(String[] args) {
class WriteThread extends Thread {
public void run() {
FinalFieldExample.writer();
class ReadThread extends Thread {
public void run() {
FinalFieldExample.reader();
new WriteThread().start();
for (int i = 0; i & 100; i++) {
new ReadThread().start();
也就是理论上存在这种可能吧。现实中,谁能写个测试程序,使得我们可以看到 f.y不等于 4??
问题补充:snake1987 写道代码我就懒得写了
首先说一下你这个代码的问题
jsr133里面说的意思你并没有完全理解,
首先,为什么会出现不等于4?
原因很简单,java的一个对象的new动作,并不是线程安全的
new这个动作你可以这样理解
1.分配空间
2.初始化该对象
3.将引用赋给你代码中的f变量
如果按这个顺序来看,是不可能出现不等于4的
但搬到并发的情况下就不一样了
java的内存模型只会保证,你在该线程内看到的似乎是一致的,但不保证在该线程外
也就是说,在执行new这个动作的线程内,这个顺序必然是按照上面的1.2.3的,但是如果使用另一个线程去观察这个线程中的对象,你有可能看到的1,3,2
这会出现什么状况?
举个简单的例子
class Test {
public int x = 1;
public int y = 2;
先执行了3,再执行2,你在某个时刻看到的会是x=1,如果没有这个过程,x可能是未初始化的,可能为0,可能是一个莫名其妙的值
你的测试的代码需要改一下,也就是读的线程需要不断地创建对象
你就能看到这个现象了
关于为什么final可以,详细看看jsr133你会知道的,呵呵,我的博客里面也稍微翻译了一下
Hi,看了你的blog,相当深刻。不过,我还是有下面的问题
问题一:你确信 f.y 之所以可能不为4,是由于你列出的 1、2、3的执行顺序导致的?而不是由于 thread-locally cache所导致的?
问题二:我无法使得demo出 f.y 不为4的情况。能否麻烦你发下测试代码,谢谢啦
这是我的代码
public class FinalFieldExample {
static FinalFieldE
public FinalFieldExample() {
static void writer() {
f = new FinalFieldExample();
static void reader() {
while (f != null) {
int i = f.x;
int j = f.y;
if (j != 4) {
System.out.println("j should be 4");
if (i != 3) {
System.out.println("i should be 3");
public static void main(String[] args) {
class WriteThread extends Thread {
public void run() {
while (true)
FinalFieldExample.writer();
class ReadThread extends Thread {
public void run() {
FinalFieldExample.reader();
for (int i = 0; i & 100; i++) {
new WriteThread().start();
for (int i = 0; i & 100; i++) {
new ReadThread().start();
采纳的答案
关于问题1,可能你没理解线程内缓存的意思吧,1,2,3过程其实跟线程内缓存是有联系的,两者并不冲突
比如说,将a变量赋值,当B线程需要读取a时,会将a的值从主存拷贝到你所说的cache中去,其实就是cpu的寄存器,如果改变了a的值,只是改变了寄存器的值,是不保证会写回主存的,所以对C线程是不可见的
1,2,3步骤说的是另一码事,是说构造器的一个实现逻辑,与cache其实并没有太大的关系
而final关键字,或者是是否值会改变,其实更重要的是构造器的实现逻辑
第二个问题,我也写了点代码尝试了一下,也无法出现想要的结果
其实原因也很简单,并发量不够,而且这个是由于jvm优化,产生的偶然性几率极小的问题,所以难以重现是正常的
学习并发的东西没必要太执着,知道原理就可以了,如果每个问题都想重现一下,那会累死去
至于证据,我想了想,可以引用jsr133 faq里面的内容
:在1.4以前,final并不保证在构造器中的并发安全,所以string会出现这样的情况,本来是"abc"的,结果读的时候却是另一个
这个问题明显是很严重的bug,
但是用1.3的人有多少人发现了这个bug?当时还不是用得好好的
以此来推出这个问题难以复现,应该是成立的
代码我就懒得写了
首先说一下你这个代码的问题
jsr133里面说的意思你并没有完全理解,
首先,为什么会出现不等于4?
原因很简单,java的一个对象的new动作,并不是线程安全的
new这个动作你可以这样理解
1.分配空间
2.初始化该对象
3.将引用赋给你代码中的f变量
如果按这个顺序来看,是不可能出现不等于4的
但搬到并发的情况下就不一样了
java的内存模型只会保证,你在该线程内看到的似乎是一致的,但不保证在该线程外
也就是说,在执行new这个动作的线程内,这个顺序必然是按照上面的1.2.3的,但是如果使用另一个线程去观察这个线程中的对象,你有可能看到的1,3,2
这会出现什么状况?
举个简单的例子
class Test {
public int x = 1;
public int y = 2;
先执行了3,再执行2,你在某个时刻看到的会是x=1,如果没有这个过程,x可能是未初始化的,可能为0,可能是一个莫名其妙的值
你的测试的代码需要改一下,也就是读的线程需要不断地创建对象
你就能看到这个现象了
关于为什么final可以,详细看看jsr133你会知道的,呵呵,我的博客里面也稍微翻译了一下
已解决问题
未解决问题鍙?渶涓

我要回帖

更多关于 对象为null或未定义 的文章

 

随机推荐