Чтобы класс был потокобезопасным, независимо от того, сколько потоков к нему обращается, его инварианты и постусловия должны выполняться.
Для этого класса, хотя методов записи нет, вам все равно нужно синхронизировать чтения.Это связано с тем, что компилятор может кэшировать переменные состояния (в данном случае name
и phone
) для каждого потока (помните, что каждый поток имеет свой собственный набор регистров).Таким образом, если один поток обновляет значение какой-либо из переменных состояния, другой поток может не увидеть его и может прочитать устаревшее значение.
Очень простой способ избежать этого - создать переменные состоянияvolatile
.Это слабый примитив синхронизации, и он не обеспечивает атомарного поведения, как synchronised
.
Вот правильный способ сделать поток этого класса безопасным:
class User {
GuardedBy("this")String name;
GuardedBy("this")String phone;
public synchronised String getName() {
return name;
}
public synchronised String getPhone() {
return phone;
}
}
Примечание: Каждая переменная состоянияМожно использовать другой замок, в зависимости от ваших требований.Я предположил, что так или иначе обе эти переменные участвуют в инварианте вместе.
Слабая синхронизация:
class User {
volatile String name;
volatile String phone;
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
Для synchronised
методов каждый раз, когда поток вызывает их, он очищает свой кэши читает последнее значение из памяти, и каждый раз, когда существует метод synchronised
, он помещает последние значения переменных в память.
Стационарное чтение может быть еще более опасным с 64b
double
и long
, поскольку запись и чтение в эти типы данных в Java не являются атомарными, и могут быть выполнены в 2 32b
операциях.Это может привести к очень плохим последствиям.
Редактировать: только что увидел, что каждый поток будет иметь свою собственную копию объекта.В этом случае синхронизация не требуется.