Olá a todos, bom tenho um programa que atua em cima de uma variável incrementando ela, porém isso é feito em uma thread separada. Em outra parte (outra thread) do programa eu decremento ela, gostaria de saber qual o efeito dessas duas threads acessarem essa variável ao mesmo tempo. Nesse caso é possível acontecer de ele realizar apenas o incremento descartando a operação de decremento?
O incremento e o decremento de uma variável int, char, short ou byte é garantido ser atômico. Isso porque são realizados usando-se uma única instrução do processador.
O incremento e o decremento de uma variável long (64 bits) não é garantido ser atômico, portanto você poderia ter uma situação em que a variável é parcialmente atualizada (porque ela é acessada em 2 acessos à memória, se usar uma JVM de 32 bits) e os dados ficarem incoerentes.
Em uma JVM de 64 bits isso provavelmente não deve ocorrer, mas isso não está especificado na especificação da JVM.
Entretanto, você DEVE indicar o atributo “volatile” a essa variável acessada por 2 threads.
Isso é porque as threads podem estar em processadores diferentes e devido ao design do cache pode ser que a alteração de uma variável, se não for marcada com “volatile”, não seja reconhecida de um processador para o outro.
http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html para detalhes sobre acesso simultâneo para incremento e decremento. (É mais seguro usar java.util.concurrent.atomic.AtomicInteger que uma variável volatile int, já que as instruções do sistema operacional que forçam que ambas as threads enxergem o mesmo valor para a variável sejam executadas.
Desde já gostaria de agradecer sua resposta e me colocar a disposição para ajuda-lo também (caso precisares ), nesse caso é uma variável int, logo então não existe forma de alguma operação deixar de ser executada. Apenas para esclarecer melhor, o programa roda em apenas um processador e está sendo usado a jre1.6.
Outra opção seria deixar o acesso a essa variável num bloco sincronizado. Mas eu também optaria pelo volatile, caso você esteja apenas fazendo essas continhas de incremento e decremento.
Tipo tava analizando a classe AtomicInteger mas acho que seria usar um canhão acelerador de particulas para matar um pernilongo.
Acredito que o volatile já seja suficiente, mas apenas esclarecendo, sem o volatile eu corro o risco de perder uma operação (exemplo um decremento)?
Sim. Sem volatile você pode estar olhando para o cache local das threads. Assim, um decremento numa thread pode não ser visto na outra. E quando a outra atualizar o valor, irá atualizar para um valor errado. O volatile pede para o Java não usar esse cache.
Isso me gerou uma dúvida. Um incremento simples:
c++;
Gera somente uma instrução de bytecode:
iinc 1 1 [c]
Mas… (sempre tem um mas) segundo o capítulo de concorrência do tutorial da Sun:
Que é apenas uma operação unária no Java eu sei. Que gera apenas um comando de bytecode também sei.
Mas afinal, é apenas uma instrução para o processador ou não?
Então nesse caso se uma thread estiver incrementando a variável, a outra ficará em espera para poder acessar o valor?
No caso do synchronized há espera. No caso do volatile, não.
Até por isso o volatile só garante thread safety caso sua variável só sofra operações atômicas. Como leitura do valor e um update simples (como um set direto ou incremento/decremento).