Для вашого конкретного прикладу: якщо не оголошено нестійким, сервер JVM може підняти файл keepRunning
вивести змінну з циклу, оскільки вона не змінена в циклі (перетворюючи її в нескінченний цикл), але клієнтська JVM цього не робить. Ось чому ви бачите різні результати.
Загальне пояснення щодо летких змінних наведено нижче:
Коли поле оголошено volatile
, компілятор та час виконання поміщаються в повідомлення про те, що ця змінна є спільною та що операції над нею не слід переупорядковувати з іншими операціями пам'яті. Енергозмінні змінні не кешуються в регістрах або кешах, де вони приховані від інших процесорів, тому зчитування мінливої змінної завжди повертає останню запис будь-яким потоком .
Ефекти видимості летких змінних виходять за межі значення самої летючої змінної. Коли потік A пише в летючу змінну, а згодом потік B зчитує ту саму змінну, значення всіх змінних, які були видимими для A до запису в летючу змінну, стають видимими для B після зчитування змінної змінної.
Найбільш поширеним використанням змінних змінних є позначка завершення, переривання або статусу:
volatile boolean flag;
while (!flag) {
}
Нестабільні змінні можна використовувати для інших видів інформації про стан, але при спробі цього потрібно бути обережнішим. Наприклад, семантика volatile недостатньо сильна, щоб зробити операцію збільшення ( count++
) атомарною, якщо ви не гарантуєте, що змінна записується лише з одного потоку.
Блокування може гарантувати як видимість, так і атомність; мінливі змінні можуть гарантувати лише видимість.
Ви можете використовувати летючі змінні лише тоді, коли виконуються всі наступні критерії:
- Записи у змінну не залежать від її поточного значення, або ви можете гарантувати, що лише один потік оновлює значення;
- Змінна не бере участі в інваріантах з іншими змінними стану; і
- Блокування не потрібно з будь-якої іншої причини під час доступу до змінної.
Порада щодо налагодження : обов’язково завжди вказуйте -server
перемикач командного рядка JVM під час виклику JVM, навіть для розробки та тестування. Сервер JVM виконує більшу оптимізацію, ніж клієнтська JVM, наприклад, піднімання змінних з циклу, які не змінені в циклі; код, який може працювати в середовищі розробки (клієнтська JVM), може зламатися в середовищі розгортання (серверна JVM).
Це уривок із "Паралельності Java на практиці" , найкращої книги, яку ви можете знайти на цю тему.