Безопасен ли доступ к двум AtomicInteger как одному потоку операций? - PullRequest
0 голосов
/ 18 сентября 2018

В параллельности 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, то также будут ситуации, когда объект может достичь недопустимого состояния.Тем не менее, я думаю, что если обе функции синхронизированы, то только один поток может выполнить любую функцию, и каждая функция имеет необходимые проверки для инварианта.Как мы можем остаться в недействительном состоянии.Может кто-нибудь продемонстрировать на примере.Что я здесь упускаю?

Если я правильно понимаю, то в чем смысл этой строки:

Вы не можете обновить один, снять и снова получить блокировку, а затемобновите остальные, так как это может привести к тому, что объект будет находиться в недопустимом состоянии после снятия блокировки.

1 Ответ

0 голосов
/ 18 сентября 2018

Из "Параллелизма Java на практике" книга:

NumberRange можно сделать потокобезопасным, используя блокировку для поддержания его инварианты, такие как защита нижнего и верхнего с общим замком. Это Также необходимо избегать публикации нижнего и верхнего уровня, чтобы клиенты не могли подрыв его инвариантов.

Это означает, что следующий код является поточно-ориентированным:

@ThreadSafe
public class NumberRange {

    @GuardedBy("this") private int lower, upper;

    public synchronized void setLower(int i) {
        if (i > upper) {
            throw new IllegalArgumentException("can't set lower to " + i + " > upper");
        }
        lower = i;
    }

    public synchronized void setUpper(int i) {
        if (i < lower) {
            throw new IllegalArgumentException("can't set upper to " + i + " < lower");
        }
        upper = i;
    }

    public synchronized boolean isInRange(int i) {
        return (i >= lower && i <= upper);
    }
}

В этом случае NumberRange обеспечивает собственную блокировку, чтобы гарантировать, что составные действия являются атомарными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...