В чем разница между энергозависимым и синхронизированным?

Я пытаюсь увидеть как volatile работает здесь. Если я заявляю cc как volatileЯ получаю вывод ниже. Я знаю, что результат выполнения потока меняется время от времени, но я где-то читал, что volatile такой же как synchronized, так почему я получаю этот вывод? И если я использую два экземпляра Thread1 это имеет значение?

    2Thread-0
    2Thread-1
    4Thread-1
    3Thread-0
    5Thread-1
    6Thread-0
    7Thread-1
    8Thread-0
    9Thread-1
    10Thread-0
    11Thread-1
    12Thread-0
public class Volexample {
    int cc=0;

    public static void main(String[] args) {
        Volexample ve=new Volexample();
        CountClass count =ve.new  CountClass();
        Thread1 t1=ve.new Thread1(count);
        Thread2 t2=ve.new Thread2(count);
        t1.start();
        t2.start();
    }

    class Thread1 extends Thread{
        CountClass count =new  CountClass();

        Thread1(CountClass count ){
            this.count=count;
        }

        @Override
        public void run() {
            /*for(int i=0;i<=5;i++)
            count.countUp();*/
            for(int i=0;i<=5;i++){
                cc++;
                System.out.println(cc + Thread.currentThread().getName());
            }
        }
    }

    class Thread2 extends Thread {
        CountClass count =new  CountClass();

        Thread2(CountClass count ){
            this.count=count;
        }

        @Override
        public void run(){
            /*for(int i=0;i<=5;i++)
            count.countUp();*/
            for(int i=0;i<=5;i++){
                cc++;
                System.out.println(cc + Thread.currentThread().getName());
            }
        }
    }

    class CountClass{
        volatile int count=0;
        void countUp(){
            count++;
            System.out.println(count + Thread.currentThread().getName());
        }
    }
}

3 ответа

В Java семантика volatile Ключевое слово очень хорошо определены. Они гарантируют, что другие потоки увидят последние изменения в переменной. Но они не делают операции чтения-изменения-записи атомарными.

Так что если i является volatile и вы делаете i++, вы гарантированно прочитали последние значения i и вам гарантировано, что другие темы увидят вашу запись в i немедленно, но вам не гарантируется, что два потока не будут чередовать свои операции чтения / изменения / записи, чтобы два приращения имели эффект одного приращения.

предполагать i является переменным целым числом, значение которого было инициализировано равным нулю, кроме записи еще не было записей, и два потока делают i++;может произойти следующее:

  1. Первый поток читает ноль, последнее значение i,
  2. Второй поток читает ноль, а также последнее значение i,
  3. Первый поток увеличивает ноль, который он прочитал, получая единицу.
  4. Второй поток увеличивает ноль, который он прочитал, также получая единицу.
  5. Первый поток записывает тот, на который рассчитал i,
  6. Второй поток записывает тот, для которого он рассчитан i,
  7. Последнее значение написано i один, так что любой поток, который обращается i сейчас увидим.

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

Ты можешь использовать synchronized формировать сложные атомные операции. Если вам просто нужны простые, вы можете использовать различные Atomic* классы, которые предоставляет Java.

Вариант использования для использования volatile будет чтение / запись из памяти, которая сопоставлена ​​с регистрами устройства, например, на микроконтроллере, где что-то кроме CPU будет считывать / записывать значения в этот адрес "памяти" и, таким образом, компилятор не следует оптимизировать эту переменную.

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

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

PS Используйте большие петли, чтобы заметить ошибки. Например, попробуйте повторить 10e9 раз.

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