并发编程:final关键字

一、构造方法溢出问题

考虑下面的代码:

num3和num4的值是否一定是1和2? num3、num4不见得一定等于1,2。

和DCL的例子类似,也就是构造方法溢出问题。 myClass = new MyClass()这行代码,分解成三个操作: 1. 分配一块内存; 2. 在内存上初始化i=1,j=2; 3. 把myClass指向这块内存。 操作2和操作3可能重排序,因此线程B可能看到未正确初始化的值。对于构造方法溢出,就是一个 对象的构造并不是“原子的”,当一个线程正在构造对象时,另外一个线程却可以读到未构造好的“一半对 象”。

二、final的happen-before语义

要解决这个问题,不止有一种办法。

办法1:给num1,num2加上volatile关键字。

办法2:为read/write方法都加上synchronized关键字。

如果num1,num2只需要初始化一次,还可以使用final关键字。

之所以能解决问题,是因为同volatile一样,final关键字也有相应的happen-before语义:

1. 对final域的写(构造方法内部),happen-before于后续对final域所在对象的读。

2. 对final域所在对象的读,happen-before于后续对final域的读。 通过这种happen-before语义的限定,保证了final域的赋值,一定在构造方法之前完成,不会出现 另外一个线程读取到了对象,但对象里面的变量却还没有初始化的情形,避免出现构造方法溢出的问题。

三、happen-before规则总结

1. 单线程中的每个操作,happen-before于该线程中任意后续操作。

2. 对volatile变量的写,happen-before于后续对这个变量的读。

3. 对synchronized的解锁,happen-before于后续对这个锁的加锁。

4. 对final变量的写,happen-before于final域对象的读,happen-before于后续对final变量的 读。

四个基本规则再加上happen-before的传递性,就构成JMM对开发者的整个承诺。在这个承诺以外 的部分,程序都可能被重排序,都需要开发者小心地处理内存可见性问题。

展开阅读全文

页面更新:2024-06-13

标签:关键字   语义   初始化   线程   变量   对象   内存   办法   操作   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top