class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
Alguém pode explicar por que a classe acima não é thread-safe?
java
multithreading
thread-safety
das kinder
fonte
fonte
increment
), seria thread-safe. Ou se você usou algum objeto de bloqueio. Como eu disse, não sei sobre Java - meu comentário vem do conhecimento de C #.synchronized
deve ser usado apenas em métodos estáticos. Portanto, em minha opinião, mesmo que você remova oincrement
método, ele ainda não é thread-safe, pois duas instâncias (que têm apenas acesso sincronizado por meio da mesma instância) podem chamar o método simultaneamente.Respostas:
Como o
increment
método é,static
ele sincronizará no objeto de classe para oThreadSafeClass
. Odecrement
método não é estático e será sincronizado na instância usada para chamá-lo. Ou seja, eles serão sincronizados em objetos diferentes e, portanto, dois threads diferentes podem executar os métodos ao mesmo tempo. Como as operações++
e--
não são atômicas, a classe não é segura para threads.Além disso, uma vez que
count
éstatic
, modificá-lo a partir dedecrement
que é um sincronizado exemplo método não é seguro uma vez que pode ser chamado em casos diferentes e modificarcount
simultaneamente dessa forma.fonte
count
éstatic
, ter um método de instânciadecrement()
é errado, mesmo se não houvessestatic
increment()
método, como dois segmentos podem invocardecrement()
em diferentes instâncias modificando o mesmo contador simultaneamente.synchronized
blocos em geral (mesmo em todo o conteúdo do método) em vez de usar o modificador no método, ou seja,synchronized(this) { ... }
esynchronized(ThreadSafeClass.class) { ... }
.++
e--
não são atômicas, mesmo ligadasvolatile int
.Synchronized
cuida do problema de leitura / atualização / gravação com++
/--
, mas astatic
palavra-chave é, bem, a chave aqui. Boa resposta!synchronized
método de instância. Só não espere "sincronizado" no método de instância para fornecer proteção para os dados estáticos. O único problema aqui é o que você disse no primeiro parágrafo: os métodos usam bloqueios diferentes na tentativa de proteger os mesmos dados e isso, é claro, não oferece proteção alguma.Você tem dois métodos sincronizados, mas um deles é estático e o outro não. Ao acessar um método sincronizado, baseado em seu tipo (estático ou não estático), um objeto diferente será bloqueado. Para um método estático, um bloqueio será colocado no objeto Class, enquanto para o bloco não estático, um bloqueio será colocado na instância da classe que executa o método. Como você tem dois objetos bloqueados diferentes, pode ter dois threads que modificam o mesmo objeto simultaneamente.
fonte
increment
sendo estático, a sincronização será feita na própria classe.decrement
sendo não estático, a sincronização será feita na instanciação do objeto, mas isso não protege nada comocount
estático.Eu gostaria de adicionar isso para declarar um contador thread-safe, acredito que a maneira mais simples é usar em
AtomicInteger
vez de um int primitivo.Deixe-me redirecioná-lo para as
java.util.concurrent.atomic
informações do pacote.fonte
As respostas dos outros são muito boas e explicam o motivo. Acabei de adicionar algo para resumir
synchronized
:synchronized
ativadofun1
efun2
é sincronizado no nível do objeto de instância.synchronized
onfun4
é sincronizado no nível do objeto de classe. Que significa:a1.fun1()
ao mesmo tempo, a última chamada será bloqueada.a1.fun1()
e o thread 2 chamaa1.fun2()
ao mesmo tempo, a última chamada será bloqueada.a1.fun1()
e encadear 2 chamadaa1.fun3()
ao mesmo tempo, sem bloqueio, os 2 métodos serão executados ao mesmo tempo.A.fun4()
, se outros threads chamamA.fun4()
ouA.fun5()
ao mesmo tempo, as últimas chamadas serão bloqueadas, poissynchronized
fun4
esteja no nível de classe.A.fun4()
, o thread 2 chamaa1.fun1()
ao mesmo tempo, sem bloqueio, os 2 métodos serão executados ao mesmo tempo.fonte
decrement
é travar uma coisa diferente paraincrement
que não impeçam um ao outro de funcionar.decrement
uma instância é bloquear algo diferente de chamardecrement
outra instância, mas eles estão afetando a mesma coisa.O primeiro significa que chamadas sobrepostas para
increment
edecrement
podem resultar em um cancelamento (correto), um incremento ou um decremento.O segundo significa que duas chamadas sobrepostas para
decrement
em instâncias diferentes podem resultar em um decréscimo duplo (correto) ou um decremento único.fonte
Uma vez que dois métodos diferentes, um é o nível de instância e outro é o nível de classe, então você precisa bloquear 2 objetos diferentes para torná-lo ThreadSafe
fonte
Conforme explicado em outras respostas, seu código não é seguro para thread, pois o método estático
increment()
bloqueia o monitor de classe e o método não estáticodecrement()
bloqueia o monitor de objeto.Para este exemplo de código, existe uma solução melhor sem o
synchronzed
uso de palavras-chave. Você tem que usar AtomicInteger para alcançar a segurança do Thread.Segure a linha usando
AtomicInteger
:fonte