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

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

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

9 ответов

Решение

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

Когда переменная изменчива, а не статична, для каждой будет одна переменная Object, Итак, на первый взгляд кажется, что нет никакой разницы от нормальной переменной, но она полностью отличается от статической. Однако даже при 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 учебный класс.

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

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

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

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

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

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

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

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

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

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

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

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

  2. volatile: это ключевое слово применимо как к переменным класса, так и к переменным экземпляра.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам