Volatile против статики в Java

Правильно ли говорить, что static означает одну копию значения для всех объектов, а volatile означает одну копию значения для всех потоков?

В любом случае, значение static переменной также будет одним значением для всех потоков, тогда почему мы должны использовать volatile?

Ответ 1

Объявление переменной static в Java означает, что будет только одна копия, независимо от того, сколько объектов класса создано. Эта переменная будет доступна даже при отсутствии Objects. Тем не менее, потоки могут иметь локально кэшированные значения.

Если переменная volatile, а не статическая, для каждой Object будет одна переменная. Таким образом, на первый взгляд кажется, что нет никакой разницы с нормальной переменной, но полностью отличается от статической. Однако даже с полями Object поток может кэшировать значение переменной локально.

Это означает, что если два потока одновременно обновляют переменную одного и того же объекта, и переменная не объявляется изменчивой, может быть случай, когда один из потоков имеет в кеше старое значение.

Даже если вы получаете доступ к значению static через несколько потоков, каждый поток может иметь свою локальную кешированную копию! Чтобы этого избежать, вы можете объявить переменную как статической volatile, и это заставит поток читать каждый раз глобальное значение.

Однако volatile не является заменой правильной синхронизации!
Например:

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.

Ответ 2

Разница между статическими и неустойчивыми:

Статическая переменная. Если два потока (предположим t1 и t2) обращаются к одному и тому же объекту и обновляют переменную, объявленную как статическую, то это означает t1 и t2 могут сделать свою собственную локальную копию одного и того же объекта (включая статические переменные) в своем соответствующем кеше, поэтому обновление, сделанное t1 статической переменной в его локальном кеше, не будет отражать в статической переменной для кэша t2.

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

Изменчивая переменная. Если два потока (предположим t1 и t2) обращаются к одному и тому же объекту и обновляют переменную, объявленную как volatile, то это означает t1 и t2 могут создавать свой собственный локальный кеш объекта , за исключением переменной, объявленной как энергозависимая. Таким образом, переменная volatile будет иметь только одну основную копию, которая будет обновляться различными потоками, а обновление, сделанное одним потоком, изменчивой переменной сразу отразится на другой теме.

Ответ 3

В дополнение к другим ответам, я хотел бы добавить одно изображение для него (рис легко понять)

enter image description here

static переменные могут быть кэшированы для отдельных потоков. В многопоточной среде, если один поток изменяет кэшированные данные, это может не отражаться на других потоках, поскольку они имеют их копию.

volatile объявление гарантирует, что потоки не будут кэшировать данные и использует только общую копию.

источник изображения

Ответ 4

Я думаю, что static и volatile имеют никакого отношения вообще. Я предлагаю вам прочитать Java-учебник, чтобы понять атомарный доступ, и зачем использовать атомарный доступ, понять, что чередуется, вы найдете ответ.

Ответ 5

Проще говоря,

  • static: static переменные связаны с классом, а не с любым объект. Каждый экземпляр класса использует переменную класса, которая находится в одном фиксированном месте в памяти

  • volatile: Это ключевое слово применимо как к классу, так и к экземпляру > переменных.

Использование изменчивых переменных уменьшает риск ошибок согласованности памяти, поскольку любая запись в изменчивую переменную устанавливает связь между событиями и последующими чтениями этой же переменной. Это означает, что изменения переменной volatile всегда видны для других потоков

Посмотрите на статью Javin Paul, чтобы лучше понять изменчивые переменные.

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

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

Здесь термин variable может быть либо переменной static (class), либо переменной instance (object).

По вашему запросу:

В любом случае значение статической переменной также будет одним значением для всех потоков, тогда почему мы должны идти на volatile?

Если мне нужна переменная instance в моем приложении, я не могу использовать переменную static. Даже в случае переменной static согласованность не гарантируется из-за кэша Thread, как показано на диаграмме.

Использование переменных volatile уменьшает риск ошибок согласованности памяти, поскольку любая запись в изменчивую переменную устанавливает связь между событиями и последующими чтениями этой же переменной. Это означает, что изменения в изменчивой переменной всегда видны для других потоков.

Что еще, это также означает, что когда поток читает изменчивую переменную, он видит не только последнее изменение в volatile, но также и побочные эффекты кода, которые привели к ошибкам согласования изменений = > все еще возможны с изменчивыми переменными. Чтобы избежать побочных эффектов, вы должны использовать синхронизированные переменные. Но в java есть лучшее решение.

Использование простого доступа к атомной переменной более эффективно, чем доступ к этим переменным через синхронизированный код

Некоторые из классов в пакете java.util.concurrent предоставляют атомарные методы, которые не зависят от синхронизации.

Обратитесь к этой статье странице высокого уровня concurrency для более подробной информации.

Особенно посмотрите Атомные переменные.

Связанные вопросы SE:

Volatile Vs Atomic

Volatile boolean vs AtomicBoolean

Разница между изменчивой и синхронизированной в Java

Ответ 6

Доступ к переменной volatile переменной будет осуществляться непосредственно из основной памяти. Он должен использоваться только в многопоточной среде. статическая переменная будет загружена один раз. Если он используется в среде с одним потоком, даже если копия переменной будет обновлена, и не будет никакого вреда для ее доступа, поскольку есть только один поток.

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

если вы ожидаете желаемых результатов от статической переменной, тогда используйте volatile со статикой в ​​многопоточности, тогда все будет разрешено.

Ответ 7

Не уверен, что статические переменные кэшируются в локальной памяти потока или НЕ. Но когда я выполнил два потока (T1, T2), обращающихся к одному и тому же объекту (obj), и когда обновление, выполненное потоком T1, до статической переменной, это отразилось на T2.

Ответ 8

Если мы объявим переменную как статическую, будет только одна копия переменной. Таким образом, всякий раз, когда разные потоки обращаются к этой переменной, для этой переменной будет только одно окончательное значение (поскольку для переменной имеется только одна ячейка памяти).

Если переменная объявлена ​​как volatile, все потоки будут иметь свою собственную копию переменной, но значение берется из основной памяти. Таким образом, значение переменной во всех потоках будет одинаковым.

Итак, в обоих случаях основное значение состоит в том, что значение переменной одинаково для всех потоков.

Ответ 9

Объявление переменной volatile гарантирует, что на разных архитектурах jvm не собирается кэшировать этот поток переменных локально