线程中的最终字段语义

时间:2012-02-29 07:29:08

标签: java multithreading thread-safety final jls

这是来自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是如何正确读取的,但是如果没有正确读取,我怎么能复制(即在构造函数完成之前引用了脚步?)

1 个答案:

答案 0 :(得分:6)

您在寻找什么

您要测试的内容称为施工期间不要发布“此”参考可见性危险。请按照提供的顺序阅读以下链接。

  1. Java theory and practice: Safe construction techniques
  2. JSR 133 (Java Memory Model) FAQ
  3. Do the ‘up to date’ guarantees for values of Java's final fields extend to indirect references?
  4. 示例代码

    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链接,它更加详细。