• ,

Volatile (пример в 17 лекции)

Вообщем есть в лекции 17 пример c volatile, там сказано, если у переменной isCancel не использовать volatile то изменяя значения данной переменной из другой нити остальные о нем не узнают, я переписал пример, только в методе run происходит вывод имени потока и значения isCancel и получилось так, что у независимо от того, будет ли стоять у переменной volatile или не будет, значение всеравно меняется для всех поток, хоть стукните но я запутался окончательно, если с синхронизацией я разобрался, то с volatile я запутался и не могу его никак применить. Может я как то не так пример создал? Я пробовал еще создать класс Clock и унаследовать от Thread, а в main создать два экземпляра и оба запустить, так же volatile не работал, помогите иначе я сойду сума )

public static void main(String[] args)
    {
       Clock n = new Clock();


        Thread thread1 = new Thread(n);
        Thread thread2 = new Thread(n);
        thread1.start();
        thread2.start();

        try
        {
            Thread.sleep(3000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        n.cancel();

    }


    public static class Clock implements Runnable
    {
        private volatile boolean isCancel = false;

        public void cancel()
        {
            this.isCancel = true;
        }

        @Override
        public void run()
        {
            while (true)
            {
               
                try
                {   System.out.println(Thread.currentThread().getName()+" "+isCancel);
                    Thread.sleep(2000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

14 комментариев

dimaMJ
Help :)
Sdu
Если я Вас правильно понял, то смысл volatile как раз-таки в противоположном.
Без volatile: каждый поток может владеть кэшированным значением переменной, при изменении значения самой переменной поток может об этом и не узнать. Т.е. к примеру Ваша переменная стала true, а поток до сих пор оперирует предыдущим значением false.
С volatile: Запрещается кэширование значения переменной. Каждый поток имеет доступ непосредственно к актуальному значению. В случае изменения, все потоки будут оперировать актуальными данными. Из минусов — относительное снижение скорости доступа к переменной, для нас пока абсолютно не критичное.
dimaMJ
это я знаю, но в ходе примера, который выше, без volatile, поток знает что изменилась переменная, может как то не так пример сделан? Все что я хотел сделать это на практике увидеть работу volatile
Sdu
Ну если Вы внимательно читали лекцию, то наверняка заметили фразу: " И тогда может возникнуть проблема". Никто не говорит, что в данном случае, без volatile, будет обязательно так. Вы можете работать без volatile и никогда не заметить проблемы. Все зависит от java-машины (как распределит потоки, в какой последовательности запустит), аппаратной платформы (например, кол-ва ядер в системе), ОС и случая =) Поэтому, однозначного примера явно демонстрирующего проблему нет.
dimaMJ
даже так) понятно, спасибо ) а я тут пытаюсь 2 часа создать проблему которую volatile может решить ахах, это я так понимаю мера предосторожности)
Sdu
Ну как мера предосторожности… Если JVM разнесет два потока по разным ядрам, а у каждого ядра свой собственный кэш, то проблема становится вполне реальной.
dimaMJ
понятно спасибо)
MrBaseMax
  • MrBaseMax
  • +2
  • Комментарий отредактирован 2015-02-07 20:21:24 пользователем MrBaseMax
И все же есть простые примеры, показывающие, как работает volatile:

public class VolatileDemo {
    
    public static void main(String[] args) {
        new VolatileDemo().start();
    }
    //----------------------------------------------

    volatile private boolean btExit = false; //попробуйте убрать volatile
    
    // запуск потоков
    public void start() {
        new Thread(gui).start();
        System.out.println("gui thread started");
        new Thread(game).start();
        System.out.println("game thread started");
    }
    
    // поток для интерфейса
    Runnable gui = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000); // типа пользователь общается с GUI и выходит из игры
            } catch (InterruptedException ignored) {}
            btExit = true; // поток game не видит это изменение без volatile
            System.out.println("gui thread finished");
        }
    };

    // поток для игры
    Runnable game = new Runnable() {
        @Override
        public void run() {
            while (!btExit) {
                // без volatile этот цикл крутится бесконечно
            }
            System.out.println("game thread finished");
        }
    };
}


И важно помнить, что volatile не на 100% решает проблему. Даже если процессору запрещено хранить в кэше эту переменную, существует небольшая вероятность конфликта данных, пока процессор считывает, меняет и возвращает ее обратно. В лекциях еще будет об этом.
Sdu
Спасибо =)
maximushka
Я понял что дело в пустом цикле… где // без volatile этот цикл крутится бесконечно
если в цикл поставить sleep или yield или другую команду связанную с текущим потоком… демонстрационный эффект пропадёт. Если можете что-то уточнить, добавить теорию, то с интересом выслушаю.
albaslug
Еще интересное поведение. Если убрать volatile для переменной, но в пустом цикле добавить пустой synchronized-блок, т. е. строчку
synchronized (this) {}

то нить будет завершаться так же, как с volatile, через две секунды.
Объясняется здесь (http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization):
After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads. Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating the local processor cache so that variables will be reloaded from main memory.
Vikentsi
  • Vikentsi
  • 0
  • Комментарий отредактирован 2017-09-13 19:39:57 пользователем Vikentsi
У в любом случае с volatile и без ее программа завершается и строчка gui thread finished на консоль выводится. В чем дело?
maksimys87
Так все верно. Строчка gui thread finished выводится. А вот строчка game thread finished не выводится. И программа не завершается. У тебя была такая строчка в консоли Process finished with exit code 0? ))
Andrei_98
В верхнем примере разве в цикле должно быть true? мне кажется что !this.isCancel
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.