Является ли id = 1 - id атомарным?
со страницы 291 OCP Java SE 6 программист практика экзаменов, вопрос 25:
public class Stone implements Runnable {
static int id = 1;
public void run() {
id = 1 - id;
if (id == 0)
pick();
else
release();
}
private static synchronized void pick() {
System.out.print("P ");
System.out.print("Q ");
}
private synchronized void release() {
System.out.print("R ");
System.out.print("S ");
}
public static void main(String[] args) {
Stone st = new Stone();
new Thread(st).start();
new Thread(st).start();
}
}
один из ответов:
выход может быть
P Q P Q
я отметил этот ответ как правильный. Мои рассуждения:
- мы начинаем два потока.
- первый входит
run()
. - по данным JLS 15.26.1, он сначала оценивает
1 - id
. Результат0
. Это хранится в стеке потока. Мы как раз собираемся сохранить это0
статическийid
, но... - бум, планировщик выбирает поток для выполнения.
- Итак, второй поток входит
run()
. Статикаid
по-прежнему1
, поэтому он выполняет методpick()
.P Q
печати. - планировщик выбирает поток для выполнения. Это займет
0
из стека и сохраняет в статическийid
. Итак, первый поток также выполняетpick()
и печатаетP Q
.
однако, в книге написано, что этот ответ неверный:
это неверно, потому что строка
id = 1 - id
меняет значениеid
между0
и1
. Один и тот же метод не может быть выполнен дважды.
я не согласен. Я думаю, что есть некоторый шанс для сценария, который я представил выше. Такой обмен не является атомарным. Я ошибаюсь?
2 ответа:
я ошибаюсь?
нет, вы абсолютно правы - как и ваш пример временной шкалы.
в дополнение к тому, что он не является атомарным, не гарантируется, что запись в
id
будет подобран другой поток в любом случае, учитывая, что нет синхронизации и поле не является изменчивым.это несколько смущает для справочного материала, как это, чтобы быть неправильным : (
на мой взгляд, ответ в практике экзаменов является правильным. В этом коде вы выполняете два потока, которые имеют доступ к одной и той же статической переменной id. Статические переменные хранятся в куче в java, а не в стеке. Порядок выполнения runnables непредсказуем.
однако, чтобы изменить значение id каждого потока:
- делает локальную копию значения, хранящегося в адресе памяти id в реестр CPU;
- выполняет операция
1 - id
. Строго говоря, здесь выполняются две операции(-id and +1)
;- перемещает результат обратно в область памяти
id
в куче.это означает, что, хотя значение id может быть изменено одновременно любым из двух потоков, изменяются только начальные и конечные значения. Промежуточные значения не будут изменены друг другом.
кроме того, анализ кода, может показать, что в любой момент времени, ID может быть только 0 или 1.
доказательство:
начальное значение id = 1; Один поток изменит его на 0 (
id = 1 - id
). И другой поток вернет его к 1.начальное значение id = 0; Один поток изменит его на 1 (
id = 1 - id
). И другой поток вернет его к 0.таким образом, значение состояния идентификатор дискретные значения 0 или 1.
конец Доказательство.
для этого кода могут быть две возможности:
возможность 1. Поток один сначала обращается к идентификатору переменной. Тогда значение id (
id = 1 - id
изменяется на 0. После этого, только методpick ()
будет выполнено, печатьP Q
. Поток два, будет оценивать id в то времяid = 0
; способrelease()
затем будет выполнена печать R S. В результате,P Q R S
будут напечатаны.возможность 2. Нитка два сначала обращается к переменной id. Тогда значение id (
id = 1 - id
изменяется на 0. После этого, только методpick ()
будет выполнено, печатьP Q
. Поток один, будет оценивать id в то времяid = 0
; способrelease()
затем будет выполнена печать R S. В результате,P Q R S
будут напечатаны.других возможностей нет. Однако, следует отметить, что варианты
P Q R S
напримерP R Q S
илиR P Q S
и т. д. может быть напечатан из-заpick()
будучи статическим методом и поэтому делится между двумя потоками. Это приводит к одновременному выполнению этого метода, что может привести к печати букв в другом порядке в зависимости от вашей платформы.однако в любом случае, не метод
pick()
илиrelease ()
быть выполнены в два раза, как они взаимоисключающие. ПоэтомуP Q P Q
не будет выхода.