В параллельности Java на практике Брайана Гетца приведен следующий пример:
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
Я понимаю, что приведенный выше код подвержен условиям гонки.
Затем он объясняет следующее:
Множественные инварианты, подобные этому, создают требования атомарности: связанные переменные должны выбираться или обновляться в одной атомарной операции.Вы не можете обновить один, снять и повторно получить блокировку, а затем обновить другие, так как это может привести к оставлению объекта в недопустимом состоянии после снятия блокировки.
Из того, что я понимаю из этого абзацав том, что если мы сделаем функции setUpper
и setLower
synchronized
, то также будут ситуации, когда объект может достичь недопустимого состояния.Тем не менее, я думаю, что если обе функции синхронизированы, то только один поток может выполнить любую функцию, и каждая функция имеет необходимые проверки для инварианта.Как мы можем остаться в недействительном состоянии.Может кто-нибудь продемонстрировать на примере.Что я здесь упускаю?
Если я правильно понимаю, то в чем смысл этой строки:
Вы не можете обновить один, снять и снова получить блокировку, а затемобновите остальные, так как это может привести к тому, что объект будет находиться в недопустимом состоянии после снятия блокировки.