Dúvida - syncronized

7 respostas
dfsilva86

Pessoal tenho o seguinte código abaixo:

public class Conta {

	private int saldo = 50;
	
	public int getSaldo(){
		return saldo;
	}
	
	public void saque(int n){
		saldo -= n;
	}
}

public class Teste implements Runnable {

	private Conta c = new Conta();
	
	public void run() {
		for (int i = 0; i < 5; i++) {
			sacar(10);
		}
	}

	public static void main(String[] args) {

		Teste t = new Teste();
		Thread one = new Thread(t);
		Thread two = new Thread(t);
		Thread three = new Thread(t);
		one.setName("Joao");
		two.setName("Maria");
		three.setName("Pedro");
		one.start();
		two.start();
		three.start();
		
	}

	private synchronized void sacar(int n) {
		
		if (c.getSaldo() >= n) {
			c.saque(n);
			System.out.println(Thread.currentThread().getName() + " sacou: "
					+ n + " reais " + "saldo em conta de: " + c.getSaldo()
					+ " reais");
			try {
				Thread.sleep(500);
			} catch (Exception e) {}
		} else {
			System.out.println("Sem saldo para o saque de "
					+ Thread.currentThread().getName() + " Saldo: "
					+ c.getSaldo() + " reais");
		}
	}
}

No livro da Kathy fala que a palavra reservada syncronized funciona como um bloqueio, e quando o Thread é suspenso ele mantém o bloqueio para que outros Threads não possam utilizar aquele método marcado como syncronized.

Só que testando esse código acima eu consegui a seguinte saída:

Joao sacou: 10 reais saldo em conta de: 40 reais
Joao sacou: 10 reais saldo em conta de: 30 reais
Maria sacou: 10 reais saldo em conta de: 20 reais
Maria sacou: 10 reais saldo em conta de: 10 reais
Maria sacou: 10 reais saldo em conta de: 0 reais
Sem saldo para o saque de Pedro Saldo: 0 reais
Sem saldo para o saque de Pedro Saldo: 0 reais
Sem saldo para o saque de Pedro Saldo: 0 reais
Sem saldo para o saque de Pedro Saldo: 0 reais
Sem saldo para o saque de Pedro Saldo: 0 reais
Sem saldo para o saque de Maria Saldo: 0 reais
Sem saldo para o saque de Maria Saldo: 0 reais
Sem saldo para o saque de Joao Saldo: 0 reais
Sem saldo para o saque de Joao Saldo: 0 reais
Sem saldo para o saque de Joao Saldo: 0 reais

A Maria pulou a vez do João antes que ele terminasse de sacar todo o dinheiro, existe alguma coisa errada?

Abraço

Editado:
Será que quando o João sai da suspensão o método é liberado novamente e o agendador decide quem executa ele primeiro?

7 Respostas

F

Fala ae David,

Também estou estudando para a prova, a parte de Threads é bem complicada, achei esse capítulo meio confuso no livro da Kathy.

Sobre o seu código, o método sincronizado é o sacar(), ou seja, ele só pode ser utilizado por um Thread por vez, mas isso não garante que o primeiro Thread vai fazer todas as chamadas a esse método antes de outro Thread chamá-lo, exatamente o que você comentou no fim da sua mensagem.

O pessoal do forum pode ajudar com mais dicas/explicações, creio para fazer o que você queria, você deve colocar o código do método run() como sincronizado, testei aqui e deu certo.

dfsilva86

fsanchez:
Fala ae David,

O pessoal do forum pode ajudar com mais dicas/explicações, creio para fazer o que você queria, você deve colocar o código do método run() como sincronizado, testei aqui e deu certo.

Kra funcionou colocando no método run(), mas será que essa é a melhor forma sincronizando o método run()? pq no livro o método run() não está sincronizado, pelo menos nos exemplos que eu vi até agora!

Valeu

F

Creio que não tem problema. Outra opção é colocar o código do método dentro de um bloco sincronizado:

public  void run() {
		synchronized (this){
			for (int i = 0; i < 5; i++) {
					sacar(10);
			}
		}
	}

Mas no final o resultado é o mesmo.
Esse exemplo é mais para treinarmos para a prova, na prática não vejo muita aplicação para isso, já que tudo será executado como um Thread só, concorda? Já que o run() está sincronizado, e estamos trabalhando sobre a mesma instância da classe nas Threads, vai dar na mesma se você chamasse o método run() diretamente, 3 vezes. Um vai executar depois do outro.
A diferença é que não sabemos (da forma atual, com os Threads) qual será executado (apesar de ter grandes chances de seguir a ordem colocada no código).

ViniGodoy

Se você colocar no run, você irá forçar que um for rode depois do outro. Ou seja, vai serializar duas threads. Aí, pergunte-se, para que criar threads, se vou rodar um código depois do outro?

Obviamente, essa não é a melhor abordagem, caso você queira uma aplicação com execução paralela.

sandeco

Olá.

Estou respondendo sua pergunta um pouco atrasado… mas é bom até para mim que tb estou estudando java, treinar as respostas.

O seu programa não roda na sequencia correta pq o agendador da maquina virtual java, não garante que
os programas rodarão em uma sequencia correta.

Não é possível prever qual será a regra pela qual o agendador, que as vezes é o agendador do sistema operacional
subjacente. O que o agendador pode garantir é que o seu thread rodará até concluir o método run(), porém sem uma
sequencia definida… a não ser que vc defina alguma regra no seu próprio software para isso

W

O código tá certo e a saída também

Funciona como bloqueio sim, imagina se 2 threads (Joao e Maria) entrassem no método ao mesmo tempo poderia haver uma inconsistência nos dados, é muito provável que acontecesse isto aqui:

Joao sacou: 10 reais saldo em conta de: 0 reais
Maria sacou: 10 reais saldo em conta de: -10 reais

T

dfsilva86:
fsanchez:
Fala ae David,

O pessoal do forum pode ajudar com mais dicas/explicações, creio para fazer o que você queria, você deve colocar o código do método run() como sincronizado, testei aqui e deu certo.

Kra funcionou colocando no método run(), mas será que essa é a melhor forma sincronizando o método run()? pq no livro o método run() não está sincronizado, pelo menos nos exemplos que eu vi até agora!

Valeu

na verdade não faz sentido algum vc sincronizar o método run…

Criado 21 de abril de 2009
Ultima resposta 16 de nov. de 2009
Respostas 7
Participantes 6