ява:`volatile `приватные поля с геттерами и сеттерами

Должны ли мы объявлять приватные поля как volatile, если экземпляры используются в нескольких потоках?

В Эффективная Java ,есть пример, когда код не работает без volatile:

import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run?
public class StopThread {
    private static boolean stopRequested; // works, if volatile is here

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while (!stopRequested)
                    i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

В пояснениях сказано, что

while(!stopRequested)
    i++;

оптимизирован примерно так:

if(!stopRequested)
    while(true)
        i++;

поэтому дальнейшие модификации stopRequestedне видны фоновому потоку, поэтому он зацикливается навсегда. (Кстати, этот код завершается без volatileв JRE7.)

Теперь рассмотрим этот класс:

public class Bean {
    private boolean field = true;

    public boolean getField() {
        return field;
    }

    public void setField(boolean value) {
        field = value;
    }
}

и нить следующим образом:

public class Worker implements Runnable {
    private Bean b;

    public Worker(Bean b) {
        this.b = b;
    }

    @Override
    public void run() {
        while(b.getField()) {
            System.err.println("Waiting...");
            try { Thread.sleep(1000); }
            catch(InterruptedException ie) { return; }
        }
    }
}

Приведенный выше код работает, как и ожидалось, без использования volatile:

public class VolatileTest {
    public static void main(String [] args) throws Exception {
        Bean b = new Bean();

        Thread t = new Thread(new Worker(b));
        t.start();
        Thread.sleep(3000);

        b.setField(false); // stops the child thread
        System.err.println("Waiting the child thread to quit");
        t.join();
        // if the code gets, here the child thread is stopped
        // and it really gets, with JRE7, 6 with -server, -client
    }
}

Я думаю, что из-за общедоступного сеттера компилятор/JVM никогда не должен оптимизировать код, который вызывает getField(), но в этой статье говорится, что существует некий шаблон «Volatile Bean» (Pattern #4 ), который следует применять для создания безопасных классов изменяемого потока -. Обновление:может быть, эта статья применима только к IBM JVM?

Вопрос в том, :, какая часть JLS явно или неявно говорит, что частные примитивные поля с общедоступными геттерами/сеттерами должны быть объявлены какvolatile(или им не нужно )?

Извините за длинный вопрос, постарался подробно объяснить проблему. Дайте мне знать, если что-то не ясно. Спасибо.

14
задан khachik 12 June 2012 в 12:36
поделиться