Допустим, я взаимодействую с системой, в которой есть два увеличивающихся счетчика, которые зависят друг от друга (эти счетчики никогда не уменьшатся):
int totalFoos; // barredFoos плюс nonBarredFoos
int barredFoos;
У меня также есть два метода:
int getTotalFoos (); // В основном сетевой вызов localhost
int getBarredFoos (); // В основном сетевой вызов localhost
Эти два счетчика сохраняются и увеличиваются кодом, к которому у меня нет доступа. Предположим, что он увеличивает оба счетчика в альтернативном потоке, но потокобезопасным способом (т. Е. В любой момент времени оба счетчика будут синхронизированы).
Каков наилучший способ получить точное количество как barredFoos, так и nonBarredFoos в один момент времени?
Совершенно наивная реализация:
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
int nonBarredFoos = totalFoos - barredFoos;
Это связано с тем, что система может увеличивать оба счетчика между двумя вызовами методов, и тогда мои две копии будут не синхронизированы, и значение barredFoos
будет больше, чем при получении totalFoos
.
Базовая реализация с двойной проверкой:
while (true) {
int totalFoos = getTotalFoos();
int barredFoos = getBarredFoos();
if (totalFoos == getTotalFoos()) {
// totalFoos did not change during fetch of barredFoos, so barredFoos should be accurate.
int nonBarredFoos = totalFoos - barredFoos;
break;
}
// totalFoos changed during fetch of barredFoos, try again
}
Теоретически это должно работать, но я не уверен, что JVM гарантирует, что это то, что на самом деле происходит на практике после оптимизации, и это принимается во внимание. Пример этих проблем приведен в http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (ссылка через Ромена Мюллера).
Учитывая методы, которые у меня есть, и предположение выше, что счетчики фактически обновляются вместе, есть ли способ, которым я могу гарантировать, что мои копии двух счетчиков синхронизированы?