Бывает раньше между потоками и атомарной переменной - PullRequest
0 голосов
/ 18 ноября 2018

Предположим, что AtomicInteger, c, является общим для двух потоков: thread1 и thread2. Thread1 устанавливает (только один раз) переменную t1, используя c.incrementAndGet(). Thread2 устанавливает (только один раз) переменную t2, используя c.incrementAndGet(). Как только t1 и t2 установлены, они не устанавливаются снова никаким другим потоком. Предположим, что после того, как thread1 установит t1, он проверяет значение t2 и получает null. Гарантируется ли, что впоследствии для t2 будет установлено более высокое значение, чем t1? (и наоборот). Другими словами, утверждение ниже всегда верно? Так почему?

AtomicInteger c = new AtomicInteger();
volatile Integer t1=null;
volatile Integer t2=null;

//Thread1
t1=c.incrementAndGet();
if(t2 == null){
  assert t2==null || t2>t1;
}

//Thread2
t2=c.incrementAndGet();
if(t1==null){
  assert t1==null || t1>t2;
}

Я полагаю, что утверждения верны по следующей причине: если t1 назначено значение с приращением c, а t2 еще не присвоено значение с помощью увеличения c, тогда, когда t2 впоследствии будет присвоено значение с помощью увеличения c, оно должно быть больше, чем значение t1.

Обновление: поскольку в соответствии с приведенным ниже правильным ответом утверждения могут не всегда выполняться, я добавил вопрос части 2: Проверка Бывает раньше между потоками и атомарной переменной Часть 2 .

Ответы [ 2 ]

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

Нет, гарантии нет.Может произойти следующее:

  • thread2: c.incrementAndGet (c равно 1, а t2 по-прежнему равно нулю, но позже будет инициализировано с 1)
  • thread1: c.incrementAndGet (c равен 2, а t1 по-прежнему равен нулю, но будет инициализирован позже 2)
  • thread1: t1 = 2
  • thread1: if (t2 == null): условие истинно.Блок if оценивается
  • thread2: t2 = 1
  • thread1: t2 == null: условие ложно, поэтому другой операнд or оценивается
  • thread1: t2> t1: false, поскольку t2 равно 1, а t1 равно 2
  • thread1: заявлено: сбой
0 голосов
/ 18 ноября 2018

Нет, они не всегда будут правдой. Нет никакой гарантии, что поток 1 будет работать до потока 2 или что операции не будут чередоваться. Если они запускаются как:

  1. нить 2 присваивает t2 = 1
  2. поток 2 выполняет if проверку, которая оценивается как true
  3. поток 1 присваивает t1 = 2
  4. поток 2 делает свое утверждение

... тогда на шаге 3 поток 2 увидит t1 != null и t2 > t1.

Аналогичным образом может завершиться поток 1.

(Как упоминает JB Nizet, даже операции, которые я написал выше, на самом деле состоят из нескольких операций. Этот уровень детализации не является строго необходимым для этого вопроса, но это является хорошей привычкой чтобы по-настоящему разбить вещи на отдельные операции, такие как incrementAndGet или присвоение, в которое оно входит. Опыт позволит вам немного отфильтровать их, когда вы захотите показать, почему что-то не работает, но показать, что это будет работать, вам действительно нужно учитывать каждую операцию.)

...