Um aviso é exibido toda vez que eu sincronizo em um campo de classe não final. Aqui está o código:
public class X
{
private Object o;
public void setO(Object o)
{
this.o = o;
}
public void x()
{
synchronized (o) // synchronization on a non-final field
{
}
}
}
então mudei a codificação da seguinte maneira:
public class X
{
private final Object o;
public X()
{
o = new Object();
}
public void x()
{
synchronized (o)
{
}
}
}
Não tenho certeza se o código acima é a maneira correta de sincronizar em um campo de classe não final. Como posso sincronizar um campo não final?
o
referido no momento em que o bloco sincronizado foi atingido. Se o objeto queo
se refere a alterações, outro thread pode vir e executar o bloco de código sincronizado.this
, também - eu recomendaria criar uma variável final na classe apenas para fins de bloqueio , o que impede que qualquer outra pessoa bloqueie no mesmo objeto.Não é realmente uma boa idéia - porque seus blocos sincronizados não são mais realmente sincronizada de uma forma consistente.
Supondo que os blocos sincronizados garantam que apenas um thread acesse alguns dados compartilhados por vez, considere:
Por que você quer que isso aconteça? Talvez haja algumas situações muito especializadas em que faça sentido ... mas você teria que me apresentar um caso de uso específico (junto com maneiras de mitigar o tipo de cenário que apresentei acima) antes de eu ficar feliz com isto.
fonte
o
) - e no meio de sua execução comece a mutar uma lista diferente. Como isso seria uma boa ideia? Acho que discordamos fundamentalmente sobre se é ou não uma boa ideia travar os objetos que você toca de outras maneiras. Eu preferiria ser capaz de raciocinar sobre meu código sem nenhum conhecimento do que outro código faz em termos de bloqueio.Concordo com um dos comentários de John: Você deve sempre usar um dummy de bloqueio final ao acessar uma variável não final para evitar inconsistências no caso de alterações de referência da variável. Portanto, em qualquer caso e como primeira regra prática:
Regra # 1: Se um campo não for final, sempre use um modelo de bloqueio final (privado).
Razão # 1: você mantém o bloqueio e altera a referência da variável por conta própria. Outro thread esperando fora do bloqueio sincronizado poderá entrar no bloco protegido.
Razão # 2: você mantém o bloqueio e outro thread altera a referência da variável. O resultado é o mesmo: outro thread pode entrar no bloco protegido.
Mas, ao usar um boneco de bloqueio final, há outro problema : você pode obter dados errados, porque seu objeto não final só será sincronizado com a RAM ao chamar synchronize (objeto). Portanto, como uma segunda regra prática:
Regra # 2: Ao bloquear um objeto não final, você sempre precisa fazer as duas coisas: Usar um manequim de bloqueio final e o bloqueio do objeto não final para o bem da sincronização de RAM. (A única alternativa será declarar todos os campos do objeto como voláteis!)
Esses bloqueios também são chamados de "bloqueios aninhados". Observe que você deve chamá-los sempre na mesma ordem, caso contrário, você obterá um bloqueio morto :
Como você pode ver, escrevo os dois cadeados diretamente na mesma linha, porque eles sempre pertencem um ao outro. Assim, você pode até fazer 10 bloqueios de aninhamento:
Observe que este código não será quebrado se você apenas adquirir um bloqueio interno como
synchronized (LOCK3)
por outras threads. Mas ele vai quebrar se você chamar em outro tópico algo como este:Há apenas uma solução alternativa para esses bloqueios aninhados ao lidar com campos não finais:
Regra # 2 - Alternativa: Declare todos os campos do objeto como voláteis. (Não vou falar aqui sobre as desvantagens de fazer isso, por exemplo, evitar qualquer armazenamento em caches de nível x, mesmo para leituras, também.)
Portanto, aioobe está certa: basta usar java.util.concurrent. Ou comece a entender tudo sobre sincronização e faça você mesmo com bloqueios aninhados. ;)
Para obter mais detalhes por que a sincronização em campos não finais quebras, dê uma olhada em meu caso de teste: https://stackoverflow.com/a/21460055/2012947
E para obter mais detalhes por que você precisa sincronizar tudo devido à RAM e aos caches, dê uma olhada aqui: https://stackoverflow.com/a/21409975/2012947
fonte
o
com um synchronized (LOCK) para estabelecer uma relação "acontece antes" entre a configuração e o objeto de leiturao
. Estou discutindo isso em uma pergunta semelhante minha: stackoverflow.com/questions/32852464/…Na verdade, não estou vendo a resposta correta aqui, ou seja, está tudo bem fazer isso.
Eu nem sei por que é um aviso, não há nada de errado com isso. A JVM garante que você obtenha algum objeto válido de volta (ou nulo) ao ler um valor, e você pode sincronizar em qualquer objeto.
Se você planeja realmente alterar o bloqueio enquanto ele está em uso (ao invés de, por exemplo, alterá-lo de um método init, antes de começar a usá-lo), você deve criar a variável que planeja alterar
volatile
. Então, tudo o que você precisa fazer é sincronizar tanto no objeto antigo quanto no novo, e você pode alterar o valor com segurança...
Lá. Não é complicado, escrever código com bloqueios (mutexes) é realmente muito fácil. Escrever código sem eles (código de bloqueio gratuito) é o que é difícil.
fonte
EDITAR: Portanto, esta solução (como sugerido por Jon Skeet) pode ter um problema com a atomicidade da implementação de "synchronized (object) {}" enquanto a referência do objeto está mudando. Eu perguntei separadamente e de acordo com o Sr. Erickson não é thread-safe - veja: A entrada em bloco sincronizado é atômica? . Portanto, tome como exemplo de como NÃO fazê-lo - com links por quê;)
Veja o código como funcionaria se synchronized () fosse atômico:
fonte
AtomicReference se adapta às suas necessidades.
Da documentação java sobre o pacote atômico :
Código de amostra:
No exemplo acima, você substitui
String
pelo seu próprioObject
Pergunta relacionada de SE:
Quando usar AtomicReference em Java?
fonte
Se
o
nunca mudar durante o tempo de vida de uma instância deX
, a segunda versão é o melhor estilo, independentemente de a sincronização estar envolvida.Agora, se há algo errado com a primeira versão, é impossível responder sem saber o que mais está acontecendo naquela aula. Eu tenderia a concordar com o compilador que ele parece sujeito a erros (não vou repetir o que os outros disseram).
fonte
Apenas adicionando meus dois centavos: recebi este aviso quando usei um componente que é instanciado pelo designer, então seu campo não pode ser realmente final, porque o construtor não pode usar parâmetros. Em outras palavras, eu tinha um campo quase final sem a palavra-chave final.
Acho que é por isso que é apenas um aviso: provavelmente você está fazendo algo errado, mas pode estar certo também.
fonte