В синхронизированном блоке Java записываются во все поля или только синхронизированная переменная?

Скажем, у вас есть этот код:

private String cachedToken;
private final Object lockObject = new Object();

....


retrieveToken(){
 synchronized(lockObject){
  if (cachedToken == null){
   cachedToken = goGetNewToken();
  }
  return cachedToken;
 }
}

Будет ли запись в cachedToken видна для всех потоков, заблокированных на lockObject?

Ответ 1

Да. Синхронизация на lockObject устанавливает Happens Before Relationship (также устанавливает барьер памяти). Это означает, что все потоки, которые впоследствии получают блокировку, будут видеть изменения, которые произошли во время блокировки ранее.

Однако для того, что стоит, ваша реализация ленивой инициализации ошибочна. Это правильный способ:

private volatile String cachedToken;

retrieveToken() {
    if (cachedToken == null) {
        synchronized(lockObject) {
            if (cachedToken == null) {
                cachedToken = goGetNewToken();
            }
        }
    }
    return cachedToken
}

Таким образом, вы должны получить блокировку всего лишь несколько раз, когда Threads начнут ее запрашивать. После этого cachedToken не будет пустым, и вам не потребуется синхронизировать.

Ответ 2

Конечно, synchronize обеспечивает две вещи:

  • Атомарность
  • Защитный барьер (что вы ожидаете в своем случае) на весь объект

В то время как, например, volatile обеспечивает защиту памяти, но не обрабатывает атомарность.