Ключове слово volatile використовується для зміни значення змінної різними потоками. Він також використовується, щоб зробити класи потокобезпечними. Це означає, що кілька потоків можуть використовувати метод і екземпляр класів одночасно без будь-яких проблем. Ключове слово volatile можна використовувати або з примітивним типом, або з об’єктами.
Ключове слово volatile не кешує значення змінної та завжди читає змінну з основної пам’яті. Ключове слово volatile не можна використовувати з класами або методами. Однак він використовується зі змінними. Це також гарантує видимість і порядок. Це запобігає компілятору змінювати порядок коду.
Вміст конкретного регістру пристрою може змінитися в будь-який час, тому вам потрібне ключове слово volatile, щоб переконатися, що такі доступи не оптимізуються компілятором.
приклад
class Test { static int var=5; }
У наведеному вище прикладі припустимо, що два потоки працюють над одним класом. Обидва потоки працюють на різних процесорах, де кожен потік має свою локальну копію var. Якщо будь-який потік змінює своє значення, ця зміна не відобразиться на вихідному в основній пам’яті. Це призводить до неузгодженості даних, оскільки інший потік не знає про змінене значення.
class Test { static volatile int var =5; }
У наведеному вище прикладі статичні змінні є членами класу, які є спільними для всіх об’єктів. В основній пам'яті є лише одна копія. Значення мінливої змінної ніколи не зберігатиметься в кеші. Усе читання та запис виконуватиметься з основної пам’яті та до неї.
Коли це використовувати?
- Ви можете використовувати змінну volatile, якщо хочете автоматично читати та записувати змінну long і double.
- Його можна використовувати як альтернативний спосіб досягнення синхронізації в Java.
- Усі потоки читачів побачать оновлене значення змінної volatile після завершення операції запису. Якщо ви не використовуєте ключове слово volatile, різні потоки читачів можуть бачити різні значення.
- Він використовується для інформування компілятора про те, що кілька потоків отримають доступ до певного оператора. Це перешкоджає компілятору виконувати будь-яке перевпорядкування чи оптимізацію.
- Якщо ви не використовуєте змінну volatile, компілятор може змінити порядок коду, вільно записуючи в кеш значення змінної volatile замість читання з основної пам’яті.
Важливі моменти
- Ви можете використовувати ключове слово volatile зі змінними. Використання ключового слова volatile із класами та методами є незаконним.
- Це гарантує, що значення змінної volatile завжди читатиметься з основної пам’яті, а не з кешу локального потоку.
- Якщо ви оголосили змінну як volatile, читання та запис є атомарними
- Це зменшує ризик помилки узгодженості пам’яті.
- Будь-який запис до змінної volatile у Java встановлює подію перед взаємозв’язком із послідовними читаннями тієї самої змінної.
- Volatile змінні завжди видимі для інших потоків.
- Volatile змінна, яка є посиланням на об’єкт, може бути нульовою.
- Якщо змінна не є спільною для кількох потоків, вам не потрібно використовувати ключове слово volatile із цією змінною.
Різниця між ключовим словом синхронізації та volatile
Ключове слово volatile не є заміною синхронізованого ключового слова, але в деяких випадках його можна використовувати як альтернативу. Існують такі відмінності:
Непостійне ключове слово | Ключове слово синхронізації |
---|---|
Ключове слово volatile є модифікатором поля. | Ключове слово Synchronized змінює блоки коду та методи. |
Потік не може бути заблокований для очікування у випадку volatile. | Потоки можуть бути заблоковані для очікування у разі синхронізації. |
Це покращує продуктивність потоку. | Синхронізовані методи погіршують продуктивність потоку. |
Він синхронізує значення однієї змінної за раз між пам’яттю потоків і основною пам’яттю. | Він синхронізує значення всіх змінних між пам'яттю потоків і основною пам'яттю. |
Volatile поля не підлягають оптимізації компілятора. | Синхронізація підлягає оптимізації компілятора. |
Приклад Volatile Keyword
У наступному прикладі ми визначили клас, який збільшує значення лічильника. Метод run () у VolatileThread.java отримує оновлене та старе значення, коли потік починає виконуватися. У основному класі ми визначаємо масив потоків.
VolatileData.java
public class VolatileData { private volatile int counter = 0; public int getCounter() { return counter; } public void increaseCounter() { ++counter; //increases the value of counter by 1 } }
VolatileThread.java
VolatileThread.java public class VolatileThread extends Thread { private final VolatileData data; public VolatileThread(VolatileData data) { this.data = data; } @Override public void run() { int oldValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: Old value = ' + oldValue); data.increaseCounter(); int newValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: New value = ' + newValue); } }
VolatileMain.java
public class VolatileMain { private final static int noOfThreads = 2; public static void main(String[] args) throws InterruptedException { VolatileData volatileData = new VolatileData(); //object of VolatileData class Thread[] threads = new Thread[noOfThreads]; //creating Thread array for(int i = 0; i <noofthreads; ++i) threads[i]="new" volatilethread(volatiledata); for(int i="0;" < noofthreads; threads[i].start(); starts all reader threads threads[i].join(); wait for } pre> <p> <strong>Output:</strong> </p> <pre> [Thread 9]: Old value = 0 [Thread 9]: New value = 1 [Thread 10]: Old value = 1 [Thread 10]: New value = 2 </pre> <hr></noofthreads;>