Se eu tiver 2 métodos sincronizados na mesma classe, mas cada um acessar variáveis diferentes, 2 threads poderão acessar esses 2 métodos ao mesmo tempo? O bloqueio ocorre no objeto ou é tão específico quanto as variáveis dentro do método sincronizado?
Exemplo:
class X {
private int a;
private int b;
public synchronized void addA(){
a++;
}
public synchronized void addB(){
b++;
}
}
2 threads podem acessar a mesma instância da classe X em execução x.addA(
) e x.addB()
ao mesmo tempo?
fonte
synchronized (this)
bloco ao redor do corpo do método. O objeto "this" não fica bloqueado, mas o objeto "this" é usado como o mutex e o corpo é impedido de executar simultaneamente com outras seções de código também sincronizadas em "this". Não tem efeito em outros campos / métodos "this" que não são sincronizados.a
eb
foram objetos, por exemplo,Integer
s, você estava sincronizando em instâncias que você está substituindo por objetos diferentes ao aplicar o++
operador.Sincronizado na declaração do método, há um açúcar sintático para isso:
Em um método estático, é um açúcar sintático para isso:
Eu acho que se os designers de Java soubessem o que é entendido agora sobre sincronização, eles não teriam adicionado o açúcar sintático, pois, na maioria das vezes, leva a implementações ruins de simultaneidade.
fonte
Em "Os tutoriais Java ™" sobre métodos sincronizados :
Em "Os tutoriais Java ™" em blocos sincronizados :
(Ênfase minha)
Suponha que você tenha 2 variáveis não intercaladas . Então, você deseja acessar cada um de diferentes threads ao mesmo tempo. Você precisa definir o bloqueio não na classe de objetos em si, mas na classe Object, como abaixo (exemplo no segundo link do Oracle):
fonte
O bloqueio acessado está no objeto, não no método. Quais variáveis são acessadas dentro do método são irrelevantes.
Adicionar "sincronizado" ao método significa que o encadeamento que executa o código deve adquirir o bloqueio no objeto antes de continuar. Adicionar "estático sincronizado" significa que o thread que está executando o código deve adquirir o bloqueio no objeto de classe antes de continuar. Como alternativa, você pode agrupar o código em um bloco como este:
para que você possa especificar o objeto cujo bloqueio deve ser adquirido.
Se você deseja evitar o bloqueio do objeto que contém, você pode escolher entre:
fonte
No link da documentação da oracle
A sincronização de métodos tem dois efeitos:
Consulte esta página de documentação para entender os bloqueios intrínsecos e o comportamento dos bloqueios.
Isso responderá à sua pergunta: No mesmo objeto x, você não pode chamar x.addA () e x.addB () ao mesmo tempo quando um dos métodos sincronizados estiver em execução.
fonte
Se você tiver alguns métodos que não estão sincronizados e estão acessando e alterando as variáveis da instância. No seu exemplo:
qualquer número de threads pode acessar esses métodos não sincronizados ao mesmo tempo em que outro thread estiver no método sincronizado do mesmo objeto e pode fazer alterações nas variáveis da instância. Por exemplo: -
Você precisa evitar o cenário em que métodos não sincronizados estão acessando as variáveis da instância e alterando-os, caso contrário não há sentido em usar métodos sincronizados.
No cenário abaixo: -
Somente um dos encadeamentos pode estar no método addA ou addB, mas ao mesmo tempo qualquer número de encadeamentos pode inserir o método changeState. Dois threads não podem inserir addA e addB ao mesmo tempo (devido ao bloqueio no nível do objeto), mas ao mesmo tempo qualquer número de threads pode inserir changeState.
fonte
Você pode fazer algo como o seguinte. Nesse caso, você está usando o bloqueio em aeb para sincronizar em vez do bloqueio em "this". Não podemos usar int porque os valores primitivos não têm bloqueios; portanto, usamos Inteiro.
fonte
Sim, ele bloqueará o outro método porque o método sincronizado se aplica ao objeto de classe WHOLE conforme apontado .... mas de qualquer maneira ele bloqueará a execução do outro thread SOMENTE enquanto executa a soma em qualquer método que addA ou addB entre, porque quando terminar ... o um thread libera o objeto e o outro thread acessa o outro método e assim por diante funcionando perfeitamente.
Quero dizer que o "sincronizado" é feito precisamente para impedir que o outro thread acesse outro enquanto estiver em uma execução de código específica. Então, finalmente, esse código funcionará bem.
Como observação final, se houver variáveis 'a' e 'b', não apenas uma variável única 'a' ou qualquer outro nome, não será necessário sincronizar esses métodos, pois é perfeitamente seguro acessar outra var (outra memória localização).
Vai funcionar bem
fonte
Este exemplo (embora não seja bonito) pode fornecer mais informações sobre o mecanismo de bloqueio. Se incrementoA é sincronizado e incrementoB não é sincronizado , o incrementoB será executado o mais rápido possível, mas se o incrementoB também for sincronizado , será necessário 'aguardar' que o incrementoA termine, antes que o incrementoB possa fazer seu trabalho.
Ambos os métodos são chamados no objeto de instância única, neste exemplo é: job e os threads 'concorrentes' são aThread e main .
Tente com ' sincronizado ' no incrementoB e sem ele, e você verá resultados diferentes. Se o incrementoB também estiver ' sincronizado ', será necessário aguardar a conclusão do incrementoA (). Execute várias vezes cada variante.
fonte
Na sincronização java, se um encadeamento desejar entrar no método de sincronização, ele obterá bloqueio em todos os métodos sincronizados desse objeto, não apenas em um método sincronizado que o encadeamento esteja usando. Portanto, um thread que executa addA () adquirirá bloqueio em addA () e addB (), pois ambos são sincronizados. Portanto, outros threads com o mesmo objeto não podem executar addB ().
fonte
Isso pode não funcionar, pois o boxe e a autoboxing de Inteiro para int e vice-versa dependem da JVM e existe uma grande possibilidade de que dois números diferentes possam obter hash no mesmo endereço se estiverem entre -128 e 127.
fonte