long и double типы не атоманры в Java
Что такое атомарность
Атомарность - это выполнение операции целиком, то есть за раз, или в случаи ошибки, не выполнение вовсе. То есть, что бы открыть замок необходимо : взять ключ, вставить в скважину, провернуть, вытянуть ключ. Операция открытие замка является атомарно выполненной лишь тогда, когда все поддействия будут тоже выполнены.
Типы данных и их размер
В языке java каждый тип данных имеет свою размерность (занимаемое в памяти место).
byte | 8 bit |
char | 16 bit |
short | 16 bit |
int | 32 bit |
float | 32 bit |
double | 64 bit |
long | 64 bit |
Как мы знаем, процессор работает с машинными словами, минимальным количеством бит, которое он может обработать за раз. Размерность машинного слова зависит от архитектуры компьютера. Самые известные из них это 32-битные и 64-битные архитектуры. Это говорит нам о том, что:
- процессор имеет размерность машинного слова 32/64 бита, то есть за одно действие он может обработать 32/64 бита соответственно
- адресное пространство, которым процессор может оперировать, составляет в первом случае 2^32 степени бит, а во втором - 2^64 степени бит.
Проблема с 32 битной архитектурой
Как мы оговорили выше, тип double и long имеют размерность 64 бита, а это значит что в 32 битной архитектуре процессора, операции с этими значаниями будут происходить в два действия. Сначала произойдет считывание/запись старших 32 битов, после - считывание младших 32 битов.
Проблема возникает как раз тогда, когда множество потоков пытается считывать одни и те же данные так, что в один момент может произойти некорректная манипуляция с данными. Например, один поток выполнил операцию запись в старшие 32 бита, а вторую операцию не закончил, и в это время второй поток начал чтение этой переменной. Второй поток и выдаст ошибку чтения, так как данные находятся в промежуточном состоянии, то есть измененные наполовину.
Решений есть несколько
- добавить модификатор volatile к этим перменным, которой говорит JVM работать с перменными, как с атомарными (так же этот модификатор говорит процессору не использовать кеш перменных для консистетности разделяемых ресурсов между потоками)
- сделать переменные закрытого типа и добавить методы доступа к нему с модификатором synchronize
- перейти с 32-битной архитектуры на 64-битную.