Initialize and change static variable in a safe way. Does this code make sense? Intellij IDEA thinks it does not

2

Hello

I'm looking for a secure way to initialize and change a static variable that will be shared by different threads that will be accessing its value.

The idea is that at some point I will check that this variable needs to be changed and I want your change to be thread safe, without affecting the threads that are accessing its current value.

The ideal was not to have the treads stop to wait for the update because the current value is still valid.

I'm using the variable in a Component ( @Service ) of Spring, in order to call the services.

The code below shows the current solution. Basically, it is a class for other classes to always get the updated token, doing the first authentication if the token is null or doing the token renewal if it is close to expiring.

Current code (not yet tested):

@AllArgsConstructor
@Service
public class TokenService {

    private static TokenData TOKEN = null;

    private final LoginService loginService;
    private final RefreshTokenService refreshTokenService;

    public String getToken() {

        if (TOKEN == null) {
            synchronized (TOKEN) {
                // preciso verificar novamente se está nulo, pois pode ter ocorrido de uma outra thread ter inicializado já o Token
                if (TOKEN == null) {
                    TOKEN = autenticar();
                }
            }
        }
        return obterToken();
    }

    private TokenData autenticar() {
        AutenticacaoResponse autenticacaoResponse = loginService.autenticar();
        return new TokenData(autenticacaoResponse.getAuthToken(), autenticacaoResponse.getIssuedAt());
    }

    private String obterToken() {
        if (isPertoExpirar()) {
            synchronized (TOKEN) {
                // preciso verificar novamente se está perto de expirar, pois pode ter ocorrido de uma outra thread ter já alterado o Token. 
                // Aqui eu até poderia abrir mão deste controle, pois não teria problema se duas ou mais threads chamassem o refresh.
                if (isPertoExpirar()) {
                    AutenticacaoResponse autenticacaoResponse = refreshTokenService.atualizar(TOKEN.getToken());
                    TOKEN = new TokenData(autenticacaoResponse.getAuthToken(), autenticacaoResponse.getIssuedAt());
                }
            }
        }
        return TOKEN.getToken();
    }

    private boolean isPertoExpirar() {
        return TOKEN.isFaltaMeiaHoraExpirar();
    }
}

My idea was to put a syncronized in the variable TOKEN , but Intellij IDEA alerts you to two issues when trying to do this:

Dereference of 'TOKEN' may produce java.lang.NullPointerException
Synchronization of a non-final field 'TOKEN'

I could try to initialize the TokenData class and know if there is a valid token inside it, but I would still be getting the second alert. I do not see much of a problem in this, but according to Intellij IDEA:

  

Reports synchronized statements where the lock expression is a reference to a non-final field. Such statements are unlikely to have useful semantics, so different threads may lock on different objects even when operating on the same object.

    
asked by anonymous 13.06.2018 / 15:00

1 answer

2

Do not synchronize over the token. Synchronize over a final object.

private static final Object lock = new Object();

synchronized (lock) {
    ...
}
    
13.06.2018 / 15:42