Acesso simultâneo (duas threads) a uma variável

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 :slight_smile: ), 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).