Конечная семантика полей в потоках - PullRequest
6 голосов
/ 29 февраля 2012

Это из 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 Ответ

6 голосов
/ 29 февраля 2012

Что вы ищете

То, что вы пытаетесь проверить, называется Не публикуйте ссылку "это" во время создания или Опасность видимости . Прочитайте следующие ссылки в порядке их предоставления.

Чтение

  1. Теория и практика Java: безопасные методы строительства
  2. Часто задаваемые вопросы по JSR 133 (модель памяти Java)
  3. Распространяются ли в «актуальных» гарантиях значения окончательных полей Java на косвенные ссылки?

Пример кода

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 , которой я поделился, это намного более подробно.

...