Что не так с кодом:
static int value = 1;
static final Object obj = new Object();
// поток 1
....
synchronized (obj) {
value++;
}
....
// поток 2
.....
int x = value;
....
Ответ.
Переменная не volatile, но ее изменение происходит в synchronized-блоке. Это гарантирует атомарность и видимость изменения только по отношению к потокам, которые читают в synchronized-блоке на том же мониторе (Смотри Вопрос с собеседования: что такое Java Memory Model и happens-before и Atomicity and Visibility). В соответствии с Java Memory Model, факт выхода из synchronized (obj) создаёт happens-before только по отношению к другим участкам кода, которые тоже синхронизируются на том же мониторе (obj). Поэтому для обеспечения видимости и атомарности нужно чтение поместить также в synchronized-блок на том же мониторе:
static int value = 1;
static final Object obj = new Object();
// поток 1
synchronized (obj) {
value++;
}
// поток 2
int x;
synchronized (obj) {
x = value;
}
В качестве альтернативы можно использовать AtomicInteger:
static final AtomicInteger value = new AtomicInteger(1);
// поток 1
value.incrementAndGet();
// поток 2
int x = value.get();
Добавление volatile в переменную обеспечит только видимость, но не атомарность операции. В данном случае у нас пишет только один поток и переменная 32-битная - поэтому эта проблема может быть не столь критичной, мы не прочитаем какое-то кривое значение, оно просто может быть не самым актуальным, если мы будем читать во время инкремента. Но если у нас будут писать уже несколько потоков, то это будет реальной проблемой, могут быть утеряны некоторые изменения. Поэтому фиксить эту проблему просто добавлением volatile - не лучшее решение.
Смотри также:
1) Разобрал 32 вопроса и задачи по Java и Многопоточность
2) Вопрос с собеседования: что такое Java Memory Model и happens-before
3) Atomicity and Visibility