Несогласованное нарушение синхронизации
Я получил это нарушение при возврате в следующем методе:
protected Token getAccessToken() {
synchronized (this) {
if (token == null || isExpired(token))
token = createToken();
}
return token; // <-- Inconsistent synchronization of blablabla.token; locked 75% of time
}
Есть ли проблемы с видимостью, связанные с token
поле? Как я понимаю после synchronized
блочный токен должен иметь свое последнее значение.
Я что-то упустил или это ложный положительный результат?
2 ответа
Учтите следующее:
- Thread1: вводит метод
- Thread2: вводит метод
- Thread1: вводит блок синхронизации, токен не равен NULL и не имеет срока действия
- Thread1: выход из блока синхронизации
- Поток 2: вводит блок синхронизации, токен не нулевой, но истек
- Thread2: назначает новый токен
- Thread1: возвращает токен (может быть новым значением, назначенным потоком 2, может быть старым значением)
- Thread2: выход из блока синхронизации
- Thread2: возвращает (новый) токен
Если вы хотите делать то, что вы делаете, то token
может потребоваться быть изменчивым (но это может быть недостаточной гарантией!), или вы всегда должны возвращать значение из синхронизированного блока, или присваивать значение token
в локальную переменную внутри синхронизированного блока и вернуть эту локальную переменную извне.
Это даже не учитывает, что другие методы могут делать с токеном в то же время. Если другой (синхронизированный или несинхронизированный) метод изменяет token
а также (например, присваивает null
), тогда вы можете быть в худшей форме, потому что вы предполагаете, что token
не является нулевым (как вы только что проверили), в то время как в действительности это может быть null
сейчас
Поток A может вернуть токен, который был только что воссоздан потоком B, потому что токен истек.
Таким образом, поток B будет записывать токен из синхронизированного блока, но поток B будет читать его из несинхронизированного блока. Так что да, могут быть проблемы. Возврат должен быть внутри синхронизированного блока.