Я попытался запустить следующий код с 'count' как volatile:
ExecutorService e = Executors.newFixedThreadPool(2);
for (int i=0; i<2; i++)
{
e.execute(new Runnable(){
public void run() {
for (int i=0; i<1000000; i++)
count++;
}
}
);
}
e.shutdown();
e.awaitTermination(1, TimeUnit.DAYS);
System.out.println(count);
count обычно заканчивается менее чем 1 000 000.
Я работаю на процессоре x86 - IntelCore 2 Duo E8400 с точкой доступа 1.6.24.Обычный аргумент потерянного обновления для оператора ++, используемого с изменчивой переменной, с целью достижения атомарного обновления, выглядит следующим образом: оба потока 1 и 2 читают значение 0 для v, оба увеличивают его на 1 и записывают значение 1.
Этот аргумент, похоже, распадается при использовании volatile на x86, поскольку:
1) Каждый доступ к переменной volatile проходит через иерархию кэша ЦП.JVM может генерировать ассемблерный код для многократного доступа к энергозависимой памяти без промежуточной загрузки / сохранения из памяти, только если он может доказать, что к энергозависимой переменной осуществляется доступ из одного потока, что здесь не так.
2)Только один ЦП может иметь определенную строку кеша в измененном состоянии, поэтому, если оба ЦП попытаются увеличить v, только одному удастся перевести строку кеша, содержащую v, в измененное состояние.У другой строки кеша станет недействительной, и только позже она войдет в измененное состояние с кешем, содержащим правильное значение 1, и обновит переменную до 2.
Чего мне здесь не хватает?