2010-03-11 2 views
230

Правильно ли сказать, что static означает одну копию со значением по всем объектам и volatile означает одну копию значения по всем темам?Летучие Vs Статические в java

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

+27

Пожалуйста, пересмотреть свой принятый ответ, так как второй ответ от @stivlo верен –

+0

Официальное объяснение изменчивости: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html #volatile – Vadzim

ответ

313

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

Когда переменная летучий и не статического, будет одна переменная для каждого 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 класс.

+0

Значит ли это, что ВСЕГДА необходимо установить volatile? –

+5

Модификатор volatile гарантирует, что любой поток, который читает поле, увидит последнее записанное значение, поэтому необходимо, если переменная распределяется между несколькими потоками, и вам нужна эта функция, это зависит от вашего варианта использования. – stivlo

+5

Что такое кеш, когда вы говорите «локально кэшированный»? Кэш CPU, какой-то JVM-кеш? –

-1

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

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

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

+14

* Если переменная объявлена ​​как volatile, все потоки будут иметь свою собственную копию переменной, но значение берется из основной памяти. * => ** right. ** * Таким образом, значение переменной в все потоки будут одинаковыми. * => ** неверно, каждый поток будет использовать одно и то же значение для одного и того же объекта, но каждый объект будет иметь свою собственную копию. ** – stivlo

273

разница между статическим и Летучие:

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

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

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

+6

Привет, @ Сом, Пожалуйста, поправьте меня, если я ошибаюсь. Но вы не думаете, что утверждение «**, но не в контексте Thread, где обновление одного потока для статической переменной немедленно отразит изменения ко всем потокам (в их локальном кеше). **« должно быть », но не в контексте Thread, где обновление одного потока до статической переменной будет ** ** <> ** немедленно отражать изменения ко всем потокам (в их локальном кеше). " – Jaikrat

+0

@Jaikrat Да, это было очень странно для меня. Я понимаю, что вы правы и что этот ответ неправильный, как написано. Я также хотел бы, чтобы меня исправили, если я ошибаюсь. – stuart

+0

@Jaikrat Threads не кэшируют статические переменные, но, ссылаясь на обновленные статические переменные. – Som

5

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

20

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

enter image description here

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

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

image source

+1

Статические переменные распределяются между объектами под потоком? Это должно читать статические переменные, которые совместно используются объектами, независимо от потоков. – cquezel

+1

«изменчивые переменные распределяются между несколькими потоками (так и объекты)». Volatile не изменяет, как переменные распределяются между несколькими потоками или объектами. Он изменяет способ использования кэша значения времени выполнения. – cquezel

+1

Ваш комментарий о статических переменных также относится к нестационарным и «будет кэшироваться» и «не будет отражать», вероятно, следует перефразировать «может быть кэширован» и «может не отображаться». – cquezel

4

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

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

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

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

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

enter image description here

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

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

Что касается вашего запроса:

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

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

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

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

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

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

Обратитесь к этой статье high level concurrency control за более подробной информацией.

Особенно взгляните на Atomic variables.

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

Volatile Vs Atomic

Volatile boolean vs AtomicBoolean

Difference between volatile and synchronized in Java

0

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

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

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

Смежные вопросы