Volatile против статики в Java - PullRequest
252 голосов
/ 11 марта 2010

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

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

Ответы [ 8 ]

344 голосов
/ 30 октября 2011

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

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

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

Даже если вы получаете доступ к статическому значению через несколько потоков, каждый поток может иметь свою локальную кэшированную копию! Чтобы избежать этого, вы можете объявить переменную как 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.

282 голосов
/ 30 августа 2012

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

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

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

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

25 голосов
/ 29 октября 2015

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

enter image description here

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

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

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

5 голосов
/ 25 августа 2013

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

4 голосов
/ 09 мая 2016

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

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

  2. volatile : Это ключевое слово применимо к переменным class и instance .

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

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

enter image description here

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

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

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

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

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

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

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

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

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

Подробнее см. В этой статье элемент управления параллельным доступом высокого уровня .

Особенно взгляните на Атомные переменные .

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

Летучие против атомных

Летучий логический или AtomicBoolean

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

0 голосов
/ 01 мая 2019

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

0 голосов
/ 27 марта 2017

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

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

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

0 голосов
/ 20 апреля 2011

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...