Летючі проти статичних на Java


265

Чи правильно сказати, що staticозначає одну копію значення для всіх об'єктів і volatileозначає одну копію значення для всіх потоків?

У будь-якому випадку staticзначення змінної також буде одним значенням для всіх потоків, тоді навіщо нам це робити volatile?


Офіційне пояснення непостійних: cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
Вадим

Відповіді:


365

Оголошення статичної змінної на Java означає, що буде лише одна копія, незалежно від того, скільки об’єктів класу створено. Змінна буде доступною навіть без Objectsстворених. Однак потоки можуть мати локально кешовані значення цього.

Коли змінна є мінливою і не статичною , для кожної буде одна змінна Object. Отже, на поверхні, здається, немає різниці від звичайної змінної, але повністю відрізняється від статичної . Однак навіть з Objectполями нитка може кешувати значення змінної локально.

Це означає, що якщо два потоки одночасно оновлюють змінну того самого Об'єкта, а змінна не оголошується мінливою, може статися випадок, коли один з потоків має кеш-пам'ять старого значення.

Навіть якщо ви отримуєте доступ до статичного значення через декілька потоків, кожен потік може мати свою локальну кешовану копію! Щоб уникнути цього, ви можете оголосити змінну статичною мінливою, і це змусить потік читати кожен раз глобальне значення.

Однак летючі не є заміною правильної синхронізації!
Наприклад:

private static volatile int counter = 0;

private void concurrentMethodWrong() {
  counter = counter + 5;
  //do something
  counter = counter - 5;
}

Виконання concurrentMethodWrongодночасно багато разів може призвести до кінцевого значення лічильника, відмінного від нуля!
Щоб вирішити проблему, вам потрібно застосувати замок:

private static final Object counterLock = new Object();

private static volatile int counter = 0;

private void concurrentMethodRight() {
  synchronized (counterLock) {
    counter = counter + 5;
  }
  //do something
  synchronized (counterLock) {
    counter = counter - 5;
  }
}

Або використовувати AtomicIntegerклас.


7
Мінливий модифікатор гарантує, що будь-який потік, який читає поле, побачить останнє записане значення, тому це необхідно, якщо змінна поділяється між декількома потоками і вам потрібна ця функція, це залежить від вашого випадку використання.
stivlo

5
Що таке кеш, коли ви говорите "локально кешований"? Кеш процесора, якийсь кеш JVM?
mert inan

6
@mertinan так, змінна може знаходитися в кеші, ближчому до процесора або ядра. Дивіться cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html для отримання більш детальної інформації.
stivlo

15
"мінливий" не означає "одну змінну на об'єкт". Відсутність "статичного" робить це. -1 за те, що не вдалося прояснити цю елементарну помилку з боку ОП.
Маркіз Лорн

27
@EJP Я думав, що у реченні "Оголошення змінної як мінливої ​​буде одна змінна для кожного Об'єкта. Тому на поверхні, здається, немає відмінності від звичайної змінної" було пояснення, що я додав, а не статичний , сміливо редагуйте статтю та вдосконалюйте формулювання, щоб зробити це зрозумілішим.
stivlo

288

Різниця між статичним і летючим:

Статична змінна : Якщо дві нитки (припустимо t1і t2) отримують доступ до одного об’єкта і оновлюють змінну, яка оголошена статичною, то це означає t1і t2може зробити власну локальну копію того самого об'єкта (включаючи статичні змінні) у відповідному кеші, тому оновіть Здійснено t1статичною змінною в її локальному кеші, не відображається в статичній змінній t2кешу.

Статичні змінні використовуються в контексті Object, де оновлення, зроблене одним об'єктом, відображатиметься у всіх інших об'єктах того ж класу, але не в контексті Thread, де оновлення одного потоку до статичної змінної відображатиме зміни негайно для всіх нитки (у своєму локальному кеші).

Летлива змінна : Якщо дві нитки (припустимо t1і t2) отримують доступ до одного об'єкта і оновлюють змінну, яка оголошена мінливою, то це означає t1і t2може зробити власний локальний кеш Об'єкта, за винятком змінної, яка оголошена мінливою . Таким чином, мінлива змінна матиме лише одну основну копію, яка буде оновлюватися різними потоками, а оновлення, зроблене одним потоком до мінливої ​​змінної, негайно відображатиметься до іншої теми.


6
Привіт @Som, виправте мене, якщо я помиляюся. Але ви не вважаєте, що вислів " але не в контексті теми, де оновлення одного потоку до статичної змінної відображатиме зміни негайно до всіх потоків (у їх локальному кеші). " Повинно бути ", але не в контексті Thread, де оновлення одного потоку до статичної змінної <<NOT>> відобразить зміни негайно до всіх потоків (у їх локальному кеші). "
Jaikrat

@Jaikrat Так, це мене дуже заплутало. Я розумію, що ти маєш рацію і що ця відповідь є неправильною, як написано. Я також хотів би бути виправленим, якщо я помиляюся.
Стюарт

@Jaikrat Нитки не кешують статичні змінні, хоча, але посилаються на оновлені статичні змінні.
Сом

@Som Тоді ви хочете виправити пункт і видалити, але не в контексті теми . Це дуже заплутано. Спасибі
Jaikrat

На жаль, ця відповідь неправильна. У сучасних процесорах навіть volatileзмінну можна розділити між різними кешами процесора. Це не представляє жодних проблем, оскільки кеш узгоджує виключне право власності на кеш-рядок перед її зміною.
Девід Шварц

32

Окрім інших відповідей, я хотів би додати до нього одне зображення (малюнок полегшує розуміння)

введіть тут опис зображення

staticзмінні можуть бути кешовані для окремих потоків. У багатопотоковому середовищі, якщо один потік модифікує кешовані дані, це може не відображатись для інших потоків, оскільки у них є його копія .

volatileдекларація гарантує, що потоки не кешуватимуть дані та використовує лише спільну копію .

джерело зображення


1
статичні змінні поділяються між об'єктами під потоком? Це має читати статичні змінні, поділяються між об'єктами всі об'єкти незалежно від потоків.
cquezel

1
"мінливі змінні поділяються між декількома потоками (тому також об'єкти)." Летючі не змінюють способи спільного використання змінних між декількома потоками чи об'єктами. Це змінює те, як час виконання дозволено кешувати значення.
cquezel

1
Ваш коментар щодо статичних змінних також застосовується до нестатичних, і "буде кешовано" і "не відображатиметься", можливо, перефразоване, "може бути кешоване" та "може не відображено".
cquezel

4
Я дуже розгубився. ця картина очистила всі мої запитання!
вин

5

Я думаю staticі volatileвзагалі не маю жодного стосунку. Я пропоную вам прочитати навчальний посібник Java, щоб зрозуміти Atomic Access , і навіщо використовувати атомний доступ, зрозуміти, що переплетено , ви знайдете відповідь.


4

Простіше кажучи,

  1. статичний : staticзмінні асоціюються з класом , а не з будь-яким об’єктом . Кожен екземпляр класу поділяє змінну класу, яка знаходиться в одному фіксованому місці в пам'яті

  2. мінливий : це ключове слово застосовно як до змінних класу, так і до примірника .

Використання змінних змінних зменшує ризик помилок узгодженості пам'яті, оскільки будь-яке записування на мінливу змінну встановлює зв'язок "буває до" з наступними зчитуваннями цієї ж змінної. Це означає, що зміни летючої змінної завжди помітні для інших потоків

Перегляньте цю статтю , Javin Paul щоб краще зрозуміти мінливі змінні.

введіть тут опис зображення

За відсутності volatileключового слова значення змінної у стеку кожного потоку може бути різним. Зробивши змінну якvolatile , всі потоки отримають однакове значення в робочій пам'яті, а помилок узгодженості пам'яті уникнуто.

Тут термін variableможе бути staticзмінним (класом) абоinstance (об'єктною) змінною.

Щодо вашого запиту:

У будь-якому випадку значення статичної змінної також буде одним значенням для всіх потоків, то чому ми повинні йти на мінливі?

Якщо мені потрібна instanceзмінна у моєму додатку, я не можу використовувати staticзмінну. Навіть у випадкуstatic змінної, консистенція не гарантована завдяки кешу потоку, як показано на схемі.

Використання volatileзмінних зменшує ризик помилок узгодженості пам'яті, оскільки будь-яке записування на мінливу змінну встановлює зв'язок "буває до" з подальшим зчитуванням тієї самої змінної. Це означає, що зміни летючої змінної завжди помітні для інших потоків.

Більше того, це також означає, що коли потік читає мінливу змінну, вона бачить не тільки останню зміну на летючу, але й побічні ефекти коду, які спричинили за собою зміни => помилки консистенції пам'яті, як і раніше, можливі з мінливими змінними . Щоб уникнути побічних ефектів, вам доведеться використовувати синхронізовані змінні. Але є краще рішення у Java.

Використання простого атомного змінного доступу є більш ефективним, ніж доступ до цих змінних через синхронізований код

Деякі з занять у java.util.concurrent пакеті містять атомні методи, які не покладаються на синхронізацію.

Докладнішу інформацію див. У цій статті про контроль рівня конкурентоспроможності високого рівня .

Особливо подивіться на атомні змінні .

Питання, пов'язані з ДП:

Леткі Vs атомні

Летючий бул проти AtomicBoolean

Різниця між летючими та синхронізованими в Java


Я дуже ціную цю відповідь. Я знав, що є volatileраніше, але ця відповідь багато в чому роз’яснює мені, чому мені ще потрібно використовувати volatileцю staticзмінну.
Chaklader Asfak Arefe

мінливий: це ключове слово застосовно як до змінних класу, так і до примірника. Вище сказане вами твердження є невірним щодо застосовного до класу. лише два ключові слова, застосовні до змінної, є мінливими та перехідними. настільки мінливі звички, що застосовуються для класу.
ASR

непостійний застосовується до змінних класу (статичних). Ознайомтеся з подвійними заблокованими одиночними посиланнями в google, і ви зможете виявити, що ваше розуміння неправильне stackoverflow.com/questions/18093735/…
Равіндра бабу

приватний статичний непостійний є дійсною декларацією.
Равіндра бабу

0

доступ до енергонезалежних змінних буде прямим з основної пам'яті. Її слід використовувати лише в багатопотокових середовищах. статична змінна буде завантажена один раз. Якщо його використовувати в середовищі одного потоку, навіть якщо копія змінної буде оновлена ​​і не буде шкоди для доступу до неї, оскільки є лише один потік.

Тепер, якщо статична змінна використовується в багатопотоковому середовищі, тоді виникнуть проблеми, якщо очікувати від неї бажаного результату. Оскільки у кожного потоку є своя копія, то будь-який приріст або декремент статичної змінної з одного потоку може не відображатися в іншій потоці.

якщо ви очікуєте бажаних результатів від статичної змінної, тоді використовуйте непостійні зі статикою в багатопотокових, тоді все буде вирішено.


0

Не впевнені, що статичні змінні є кешованими у локальній пам'яті потоку чи НЕ. Але коли я виконав два потоки (T1, T2), звертаючись до одного об’єкта (obj), і коли оновлення, зроблене потоком T1, до статичної змінної, воно відобразилося в T2.


-2

Якщо ми оголосимо змінну статичною, буде лише одна копія змінної. Отже, кожного разу, коли різні потоки отримують доступ до цієї змінної, для змінної буде лише одне кінцеве значення (оскільки для змінної існує лише одне місце пам'яті).

Якщо змінна оголошена мінливою, всі потоки матимуть власну копію змінної, але значення береться з основної пам'яті. Отже, значення змінної у всіх потоках буде однаковим.

Отже, в обох випадках головний момент полягає в тому, що значення змінної однакове для всіх потоків.


15
Якщо змінна оголошена мінливою, всі потоки матимуть власну копію змінної, але значення береться з основної пам'яті. => справа. Отже, значення змінної у всіх потоках буде однаковим. => неправильно, кожен потік буде використовувати одне і те ж значення для одного і того ж Об'єкта, але кожен Об'єкт матиме власну копію.
stivlo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.