这是来自JLS 17.5:
最终字段的使用模型很简单。设置最终字段 该对象的构造函数中的对象。 不要写对象的引用 构造在另一个线程可以在对象的构造函数之前看到它的位置 已完成。如果遵循此规则,那么当另一个线程看到该对象时, 该线程将始终看到该对象的最终版本的正确构造版本 领域。它还将查看最终引用的任何对象或数组的版本 至少与最终字段一样最新的字段。
JLS 17.5中的讨论包括以下示例代码:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
我尝试重复使用此代码来复制上面的情况,这就是我所拥有的:
public class FinalFieldThread extends Thread {
public static void main(String[] args) {
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB();
threadB.start();
threadA.start();
//threadB.start();
}
}
class ThreadA extends Thread {
@Override
public void run() {
System.out.println("ThreadA");
FinalFieldExample.writer();
}
}
class ThreadB extends Thread {
@Override
public void run() {
System.out.println("ThreadB");
FinalFieldExample.reader();
}
}
我可以测试final是如何正确读取的,但是如果没有正确读取,我怎么能复制(即在构造函数完成之前引用了脚步?)
答案 0 :(得分:6)
您要测试的内容称为施工期间不要发布“此”参考或可见性危险。请按照提供的顺序阅读以下链接。
class FinalField
{
final int x;
int y;
public FinalField()
{
Thread t = new Thread(new TestThread(this));
t.start();
y = 4;
x = 3;
}
}
class TestThread implements Runnable
{
FinalField f;
TestThread(FinalField f)
{
if(f.x != 3)
System.out.println("value of x = " + f.x);
this.f = f;
}
public void run()
{
if(f.x != 3)
System.out.println("value of x = " + f.x);
}
}
public class Test
{
public static void main(String[] args)
{
for(int i=0; i<100; i++)
{
new FinalField();
}
}
}
<强> Output
强>
value of x = 0
value of x = 0
value of x = 0
.
.
.
value of x = 0
value of x = 0
value of x = 0
当我访问我的线程的构造函数中的最后一个字段时,那时final
字段x
未正确初始化,这就是我们获得0
的原因。 Whereas
当我访问run()
中的相同字段时,那时final
字段x
被初始化到3
。发生这种情况的原因是escaping
对象的FinalField
引用。阅读我分享的1st链接,它更加详细。