Модификатор volatile
гарантирует видимость операций с полем и сохранение их последовательности. Волатильную переменную можно, например, использовать как флаг завершения работы потока:
public class Main {
private static volatile boolean run = true;
public static void main(String[] args) throws Exception {
new Thread(() -> {
long x = 0;
while (run) {
System.out.println(++x);
try {
Thread.sleep(1000);
}
catch (InterruptedException exc) {}
}
}).start();
Scanner scanner = new Scanner(System.in);
while (run) {
String line = scanner.nextLine();
if ("exit".equals(line))
run = false;
}
}
}
Без модификатора volatile
у вас нет гарантии, что поток выводящий значения переменной x
когда-нибудь заметит, что главный поток изменил состояние переменной run
.
Синхронизация гарантирует видимость операций, сохранение их последовательности и атомарность.
public class Main {
private static int x = 0;
private static int y = 1000;
private static synchronized void transfer() {
++x;
--y;
}
public static void main(String[] args) throws Exception {
for (int a = 0; a < 10; a++) {
new Thread(() -> {
for (int b = 0; b < 100; b++) {
transfer();
}
}).start();
}
System.out.println(x);
System.out.println(y);
}
}
Без модификатора synchronized
у вас нет гарантии, что значение переменной x
будет увеличено на столько же, на сколько уменьшено значение переменной y
, так как порядок и продолжительность выполнения потоков непредсказуемы.
Во втором примере volatile
не поможет. В первом может помочь использование синхронизации, но тогда на каждой итерации один из потоков будет блокировать мьютекс, а второй потом будет останавливаться, пока мьютекс не будет освобождён. Это существенно медленнее проверки состояния волатильной переменной.
Вам почти правильно ответили. Только оптимизации несколько другого рода. Дело в том, что переменная может быть закеширована для более быстрого доступа. Вариантов куча где именно. Например она может быть закеширована для каждого процессора отдельно, может быть закеширована в потоке, в принципе это зависит от того, какая реализация JVM используется.
Volatile просто запрещает подобное кэширование.
Теперь как его использовать. Volatile работает быстрее, потому что никаких блокировок не происходит. В этом его преимущество перед synchronized.
Работа со ссылками атомарна, поэтому там в теории это может быть актуальным.
А так да, конструкция относительно редкая, обычно при многопоточности используется что-то специфическое, дабы упростить работу с ней.
UPD. Там "на заборах" пишут, что long и double которые не обязательно атомарные, становятся атомарными с volatile. Но официального подтверждения я этому не нашел, если у кого то есть точная информация по этому поводу, будет круто
volatile- это не атомарность, а просто переменная, которую компилятор не должен оптимизировать.
Например, вот вам код:
int val = 16;
val = val << 1;
val = val >> 1;
в этом случае компилятор не будет делать сдвиги, а просто оставит значение 16.
Если же вы определите, что переменная volatile, то будут сделано оба сдвига.
Как я понимаю это используется только в программировании для чипов.
UPDATE:
Компилятор старается оптимизировать весь код таким образом, что-бы он был более эфективным. В случае с примером там производится две операции, взаимно-исключающие друг друга. Тоесть сначала происходит сдвиг значения 16 в лево на один бит (получилось 32), потом идет сдвиг 32-х в право на один бит (получилось снова 16). Таким образом, если переменная val, не указазана как volatile, то компилятор просто не делает этих двух сдвигов - для него будто бы ничего не происходит, а значит лишние операции. Но в разработке встраиваемых систем это может быть переключение ножки, для включение какого-то дгугого элемнета - вот там и используют volatile.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Почему completableFuture использует вместо привычного callable supplier, зачем так путать людей привыкших к старому доброму callable?
Собрал в jar проект, который работает с файлами sqlite (выполняется просто запрос "select id from user")На компьютере, где все собирал, все прекрасно работает
Есть InputStream в котором записана последовательность известной длины 32 битных чисел в LittleEndian порядке байт