A redefinição de um campo do preguiçoso-carregado com o verifique duas vezes idioma
-
08-07-2019 - |
Pergunta
Considerar o "double-check expressão para a inicialização lenta de campos de instância":
// Item 71 in Effective Java copied from esta entrevista com Bloch. private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { // First check (no locking) synchronized(this) { result = field; if (result == null) // Second check (with locking) field = result = computeFieldValue(); } } return result; }
Eu quero ser capaz de restaurar o campo de uma forma segura (forçá-lo a carregar novamente a partir do banco de dados, no meu caso).Eu suponho que nós poderíamos fazer isso por ter um método de reposição:
void reset() { field = null; }
Esta é a forma padrão de fazer a reposição de campo?Será que é seguro?Qualquer armadilhas?Eu estou perguntando porque Bloch deu o seguinte aviso sobre verificou carregamento lento:"A expressão idiomática é muito rápido, mas também complicado e delicado, para não ser tentado modificá-lo de qualquer forma.Basta copiar e colar -- normalmente não é uma boa idéia, mas apropriada aqui."
Obrigado antecipadamente, Playa do Himalaia.
Solução
Sim, esta é thread-safe.
O bloco sincronizado é evitar que vários segmentos de computeFieldValue()
chamado desnecessariamente. Desde field
é volátil, os acessos em reset
e getField
são todos bem-ordenada.
Se a primeira verificação é não nulo, getField
é feito; result
é retornado.
Caso contrário, um bloqueio é adquirido, excluindo qualquer outro segmento que pode definir o campo para não nulo, mas permitir a qualquer segmento para set field
como nulo. Se qualquer thread faz field
definido como nulo, nada deve ter mudado; essa é a condição que tem o fio para dentro do bloco sincronizado. Se outro segmento já havia adquirido o bloqueio após o check do segmento atual, e definir o campo para um valor não nulo, a segunda verificação irá detectar isso.
Outras dicas
Eu acho que isso deve ser seguro, mas só porque você está armazenando o campo em uma variável local. Depois de feito isso, não há nenhuma maneira para a referência de variável local para a mudança magicamente como nulo, mesmo se outro segmento está redefinindo valor a meio caminho do campo através.
Parece que isso vai funcionar, desde que o método de reposição é o método reset()
listados acima. No entanto, se o método reset()
instancia um novo objeto (como abaixo), você não poderia acabar potencialmente retornar algo diferente do que você pretende?
void reset() {
field = new FieldType();
}
Eu acho que depende exatamente o que você quer dizer com thread-safe.
Você pode acabar com uma situação em que uma primeira instância é usado depois de um segundo. Isso pode ser aprovado, ou talvez não.
Eu acho reset() o método não é correta.Se você ler Item 71, você vai encontrar:
Este código pode parecer um pouco complicada.Em particular, a necessidade de que os variável resultado pode não ser claro.O que esta variável não é para garantir que o campo é somente leitura uma vez que no caso comum onde isso já inicializada.
A inicialização lenta não suponha que campo talvez alterado.Se o campo será definido como null entre estes operadores:
FieldType result = field;
if (result == null) { // First check (no locking)
getField() fornecer resultado incorreto.