Ajuda com o uso de multiplas threads para um mesmo objeto [região crítica]

Pessoal, estou na última sessão da apostila Caelum FJ-11, mas uma dúvida surgiu.

A apostila apresenta o caso de duas Threads estarem acessando um mesmo objeto ao mesmo tempo (concorrência), e mudando atributos do mesmo, podendo gerar algumas inconsistência de acordo com a ordem de execução do escalonador.

Ele propoe um problema de uma conta bancária, onde uma Thread atualiza a taixa da conta, e outra permite que os clientes efetuem depositos e saques.

Nesse caso pode haver algum conflito se o sistema atualizar a tarifa de um cliente, e o mesmo efetuar um deposito no exato momento (tambem dependendo da ordem de execução do escalonador).

No entanto a apostila não implementa esse código, ela simplesmente apresenta a funcação synchronized que resguardar todos os parametros da classe (this), ou se quiser, somente algum(s) dele(s) para esses casos.

Minha dúvida então surgi ai, como toda Thread tem como ponto inicial o metodo Run que é obrigatório devido a interface Runnabl (ou Thread), como eu determino que a Thread x do objeto x vai efetuar um deposito, enquanto a Thread y do objeto x vai efetuar um ajuste de taxa?

Sei que posso mandar um parametro númerico para o metodo Run, e depois faço um switch, ou uma série de IFs para funilar o processo, porem gostaria de criar uma Thread direta do metodo da classe, entendem?

Eu tentei fazer isso aqui, usei o helper do eclipse, mas ele tenta mudar a sintaxe dos metodos para retornar um objeto Runnable… e não funciona.

Existe outra forma de resolver esse problema?

Muito grato,

Daniel

Segue código fonte:

[code]class Conta implements Runnable{

private double saldo;

// outros metodos e atributos...
public void atualiza(double taxa)  {
	synchronized (this) {
		double saldoAtualizado = this.saldo * (1 + taxa);
		this.saldo = saldoAtualizado;
		
	}
}

public void deposita(double valor) {
	synchronized (this) {
		double novoSaldo = this.saldo + valor;
		this.saldo = novoSaldo;
		
	}
}

public class Threads {

public static void main(String[] args) {
    
Conta objConta = new Conta();
    Thread threadConta = new Thread(objConta);
    threadConta.start();    

    Thread threadContaAux = new Thread(objConta);
    threadContaAux.start();    

  }

}
[/code]

Alguem tem uma dica? É assim mesmo que deve ser feito, pelo metodo Run?

Toda lógica que rodará em uma Thread diferente deve está dentro do método run() , mas é claro que o ideal é colocar essa lógica em métodos aí vc chama dentro do método run().
Quando vc passa uma instancia de Runnable para Thread e ela contem métodos synchronized(this) a Thread vai obter o bloqueio do objeto e nenhuma outra Thread poderá acessar qualquer método synchronized deste objeto até que a o bloqueio seja liberado.
E sim é pelo método run().

Obrigado, então vou passar um parametro mesmo.


Atualização: Tentei passar o parametro pelo Run() e não funciona, ele trata como se fosse qualquer outro metodo e pede para retornar um tipo Runnable.

Procurei na internet mais a fundo, (GOOGLE: Thread parameters java) e em diversos foruns o pessoal sugeriu setar o objeto antes de iniciar a Thread, ou se não usar Pipe.

De qualquer forma, obrigado pela ajuda.

abrs

Pessoal,

Desculpe re-viver esse tópico, mas estou passando por um problema com minhas Threads.

Essa código esta apresentando a saida:
Saldo Desposita: 900.0
Saldo Desposita: 1800.0

A saida não deveria ser:
Saldo Desposita: 150.0
Saldo Desposita: 1050.0
?

Quando eu efetuo o debug do código roda 100%, mas quando mando compilar no eclipse ele me da essa saida… parece que o código não roda linearmente

[code]public class Threads {

public static void main(String[] args) {
     		
    ContaAux moveConta = new ContaAux();
 
moveConta.setVal_deposito(150);
    Thread threadConta = new Thread(moveConta);
    threadConta.start();
    
moveConta.setVal_deposito(900);
    Thread threadContaAux = new Thread(moveConta);
    threadContaAux.start();
    
 }

}

class ContaAux implements Runnable{

private double saldo;
private double taxa;
private double val_deposito;

public synchronized void atualiza(double taxa)  {
	
		double saldoAtualizado = this.saldo * (1 + taxa);
		this.saldo = saldoAtualizado;
		System.out.println("Saldo Reajuste: " + this.saldo);
		
}

public synchronized void deposita(double valor) {
	
		double novoSaldo = this.saldo + valor;
		this.saldo = novoSaldo;
		System.out.println("Saldo Desposita: " + this.saldo);
		
}

public void setTaxa(double taxa) {
	this.taxa = taxa;
}

public void setVal_deposito(double val_deposito) {
	this.val_deposito = val_deposito;
}

public void run() {
	
	deposita(this.val_deposito);

}

}[/code]

O método run sobrescrito de Runnable não tem parâmetro se você colocar parâmetro vai ser uma sobrecarga e não rodará como esperado pois a Thread não o chamará.
Vamos lá vou testar seu código aqui para ver.

Obrigado Anjo,

Esse caso da atualização da taxa bancárias esta dando um nó na minha cabeça.

Vou por o texto do exercicio, obrigado pelo auxilio:

Apostila: http://www.caelum.com.br/curso/fj-11-java-orientacao-objetos/
página 225 a 228

Já encontrei o problema, quando vc trabalha com Thread não pode esperar que as coisas sejam executadas na hora em que são chamadas.
Olhe bem o que vc fez.
Na linha 7 vc chama moveConta.setVal_deposito(150); blz setou o valor e em seguida chamou o threadConta.start(); na linha 9, mas isso não garante que a Thread começará a sua execução neste momento e como vc viu não começou.
Na linha 11 vc chama moveConta.setVal_deposito(900); e modificou o valor quando vc modificou o valor provavelmente a primeira Thread entrou em execução com o valor 900.
Partir daí vc já deve ter entendido.

Não é o jeito certo de fazer mas adicione isso “threadConta.join();” na linha 10 e vc verá como o resultado muda.
OBS: o método join() lança uma exceção então adicione isso “throws InterruptedException” no frente do método main.
Ficando assim:

[code] public static void main(String[] args) throws InterruptedException {
ContaAux moveConta = new ContaAux();

moveConta.setVal_deposito(150);  
    Thread threadConta = new Thread(moveConta);  
    threadConta.start();
    threadConta.join();
      
moveConta.setVal_deposito(900);  
    Thread threadContaAux = new Thread(moveConta);  
    threadContaAux.start();  
}[/code]

Esta correto.

Outra solução para esse problema seria usar o Thread.sleep(time) entre as Threads, mas o mais garantido é o join mesmo.

Realmente, eu tinha esquecido que o escalonador pode fazer essas ações fora de ordem.

Bom, problema resolvido, muito obrigado AnjoVingador, agora não tem mais erro.

Grato

Estamos aqui ajudar mesmo. :slight_smile:
Mude o título do tópico e acrescente resolvido no primeiro post.
Fique com Deus.

Boa noite, dertyu765!

Só uma sugestão…

Não seria mais interessante fazer assim?[code] public synchronized void atualiza(double taxa) {

		this.saldo *= (1 + taxa);
		System.out.println("Saldo Reajuste: " + this.saldo);
		
}[/code]Tanto no método [i]atualiza[/i] quanto no método [i]deposita[/i], deixar de criar as variáveis, que só são do escopo do método e que não fazem nada além receber o resultado da operação, e fazer uma operação de atribuição para a variável da classe [i]saldo[/i].

Gostaria da opinião de vocês…

Abraço!