Подтвердить что ты не робот

Волатированное ключевое слово в Java - Уточнение

Я действительно смущен тем, что я читал о приложениях летучего ключевого слова в java.

  • Правильно ли указано следующее утверждение? "запись в изменчивое поле происходит до каждого последующего чтения того же поля"

  • В идеале, когда используется ключевое слово volatile?

  • В чем разница между:

    class TestClass
    {  private int x;
    
       synchronized int get(){return x;}
       synchronized void set(int x){this.x = x;}
    
    }
    

и

class TestClass
{  private volatile int x;

   int get(){return x;}
   void set(int x){this.x = x;}

}
4b9b3361

Ответ 1

volatile - модификатор поля , а синхронизированный изменяет кодовые блоки и методы. > . Таким образом, мы можем указать три варианта простого элемента доступа, используя эти два ключевых слова:

     int i1;
     int geti1() {return i1;}

     volatile int i2;
     int geti2() {return i2;}

     int i3;
     synchronized int geti3() {return i3;}

geti1() обращается к значению, которое в настоящее время хранится в i1 в текущем потоке. В потоках могут быть локальные копии переменных, и данные не должны совпадать с данными, содержащимися в других потоках. В частности, другой поток может обновить i1 в нем поток, но значение в текущем потоке может быть отличается от этого обновленного значения. На самом деле у Java есть идея "основной" памяти, и это память, которая содержит текущее "правильное" значение для переменных. Темы могут иметь собственную копию данных для переменных, а копия потока может отличаться от "основной" памяти. Таким образом, на самом деле "основная" память может иметь значение 1 для i1, для того чтобы thread1 имел значение 2 для i1 и для thread2 иметь значение 3 для i1, если thread1 и thread2 обновлены i1, но эти обновленные значения еще не были распространены на "главную" память или другие потоки.

С другой стороны, geti2() эффективно обращается к значению i2 из "основной" памяти. Изменчивой переменной не разрешается иметь локальную копию переменной, которая отличается от текущей в "основной" памяти. Эффективно переменная, объявленная volatile, должна иметь синхронизацию данных по всем потокам, так что всякий раз, когда вы получаете доступ или обновляете переменную в любом потоке, все остальные потоки сразу видят одно и то же значение. Вообще-то волатильные переменные имеют более высокий доступ и накладные расходы на обновление, чем "простые" переменные. В общем случае потокам разрешено иметь собственную копию данных для повышения эффективности.

Существует два различия между волютильными и синхронизированными.

Во-первых, синхронизация получает и освобождает блокировки на мониторах, которые могут заставить только один поток одновременно выполнять блок кода. Это довольно хорошо известный аспект для синхронизации. Но синхронизируется и синхронизирует память. Фактически синхронизация синхронизирует всю память потока с "основной" памятью. Поэтому выполнение geti3() выполняет следующие действия:

  • Нить получает блокировку на мониторе для этого объекта.
  • В памяти потока сбрасываются все его переменные, т.е. все его переменные эффективно считываются из "основной" памяти.
  • Выполняется блок кода (в этом случае устанавливается значение возврата к текущему значению i3, которое могло быть только reset из основной памяти).
  • (Любые изменения переменных обычно записываются в "главную" память, но для geti3() у нас нет изменений.)
  • Этот поток освобождает блокировку на мониторе для объекта.

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

http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html

Ответ 2

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

Ответ 3

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

Сегодня волатильное ключевое слово syas две очень важные вещи:

  • Не беспокойтесь о том, но знайте, что при чтении нестабильного поля вы всегда будете иметь самое последнее значение.
  • Компилятор не может повторно заказать изменчивое чтение/запись, чтобы поддерживать порядок в программе.

Ответ 4

С точки зрения клиента частное поле volatile скрыто от открытого интерфейса, тогда как синхронизированные методы более заметны.

Ответ 5

Ответьте на часть 3 вашего вопроса и частично на часть 2.

Нет функциональной разницы между synchronized и volatile образцами.

Однако у каждого из них есть свои недостатки с точки зрения производительности. В некоторых случаях производительность volatile может быть действительно хуже, чем просто использование synchronized или других примитивов из java.util.concurrent. Для обсуждения этого см. → Почему переменные в Java нестабильны по умолчанию?.

Ответ 6

Ответа на этот вопрос Kerem Baydoğan совершенно прав. Я просто хочу дать практический пример того, что предлагает нам volatile.

Во-первых, у нас есть счетчик, smth вроде

public class Counter {
    private int x;
    public int getX() { return x; }
    public void increment() { x++; }
}

И некоторые Runnable задачи, которые увеличивают значение x

@Override
public void run() {
    for (N) {
            int oldValue = counter.getX();
            counter.increment();
            int new value = counter.getX();
        }

    }
}

При отсутствии синхронизации будет помеха между потоками и просто не будет работать

самый простой способ решить эту проблему:

public class Counter {
    private int x;
    public synchronized int getX() { return x; }
    public synchronized void increment() { x++; }
}

На самом деле, чтобы заставить систему сломаться, я делаю Thread.sleep перед чтением и записью x, просто представьте, что это BD или огромная задача для решения.


Теперь для чего полезен volatile? Там есть много хороших статей: изменчивая статья или этот вопрос

синхронизация доступа к общему ресурсу - это не ответ, но хороший выбор для удержания флага для остановки потоков

Я наш пред. Например, представьте, что мы хотим увеличить значение переменной до 100, простым способом может быть флаг volatile boolean. Пример:

private volatile boolean stop;

@Override
public void run() {
    while(!stop) {
        int oldValue = counter.getX();
        if (oldValue == 100) {
             stop = true;
        } else {
             counter.increment();
             int new value = counter.getX();
        }       
     }
}

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