Threads - Exercício de Faculdade

34 respostas
J

Já vou logo avisando que não quero que resolvam para mim o exercício, eheeh, gostaria apenas de algumas opniões, talvez um "norte". Já pesquisei e pesquisei muuuito e não consigo resolver a bagaça, resolvi postar aqui também pq em minha pesquisa acabei encontrando informações que divergem frontalmente com as quais o professor nos passa. Passo a desecrever abaixo a descrição do problema, algumas observações em relação às restrições e, em seguida, o código, e, após este último, a descrição de minhas tentativas.

Modifique a implementação da classe AgenteConta utilizando somente travas explicitas de modo que, ao final da execução, suas quatro instâncias tenham o mesmo saldo (1.000.000,00). É proibido modifcar o método main() e é proibido também usar trava ou monitor global único para todas as threads. Também é probibido mudar a lógica da aplicação (i.e., cada instancia de AgenteConta deve continuar debitando das outras contas, creditando para si e, em seguida, transferindo uma unidade para cada uma das outras contas) e a sequência de operações realizadas no método run().

[color=darkblue] Considerações sobre as restrições:[/color]
Como o professor viu que ninguém estava conseguindo resultados resolveu afrouxar as restrições e disse que poderiamos fazer uma primeira versão quebrando uma ou outra restrição, como por exemplo usar monitores globais.

Sobre a divergencia com o que professor considera como travas explicitas
Segundo ele, travas explicitas são travas em que se usa synchronized/wait/sleep em blocos de codigos ou em métodos, em minha pesquisa encontrei exatamente o contrário, várias fontes dizem que estes são locks implícitos, que travas explícitas seriam quando instanciamos uma implementação da interface Lock. Normalmente, instancia-se ReentrantLock. O que me dizem?

[color=darkred]O Código original:[/color]

public class AgenteConta implements Runnable
{
	private double saldo = 1000000.0;

	private AgenteConta[] contas = new AgenteConta[ 3 ];

	public AgenteConta( )
	{
	}

	public AgenteConta( AgenteConta c1, AgenteConta c2, AgenteConta c3 )
	{
		contas[ 0 ] = c1;
		contas[ 1 ] = c2;
		contas[ 2 ] = c3;
	}

	public void debitar( double valor )
	{
		if ( valor <= saldo )
		{
			saldo -= valor;
		}
		else
		{
			System.out.println( "Saldo Insuficiente" );
		}
	}

	public void creditar( double valor )
	{
		saldo += valor;
	}

	public void transferir( AgenteConta c, double quantia )
	{
		c.saldo = c.saldo - quantia;
		this.saldo += quantia;
	}

	public void run( )
	{
		for ( long i = 0; i < 1000000; i++ )
		{
			contas[ 0 ].debitar( 1 );
			contas[ 1 ].debitar( 1 );
			contas[ 2 ].debitar( 1 );
			this.creditar( 3 );
			this.transferir( contas[ 0 ], 1 );
			this.transferir( contas[ 1 ], 1 );
			this.transferir( contas[ 2 ], 1 );
		}
	}

	public static void main( String[] args )
	{
		long t = System.currentTimeMillis( );
		AgenteConta c1 = new AgenteConta( );
		AgenteConta c2 = new AgenteConta( );
		AgenteConta c3 = new AgenteConta( );
		AgenteConta c4 = new AgenteConta( c1, c2, c3 );
		c1.contas = new AgenteConta[] { c2, c3, c4 };
		c2.contas = new AgenteConta[] { c1, c3, c4 };
		c3.contas = new AgenteConta[] { c1, c2, c4 };
		Thread t1 = new Thread( c1 );
		Thread t2 = new Thread( c2 );
		Thread t3 = new Thread( c3 );
		Thread t4 = new Thread( c4 );
		t1.start( );
		t2.start( );
		t3.start( );
		t4.start( );
		try
		{
			t1.join( );
			t2.join( );
			t3.join( );
			t4.join( );
		}
		catch ( InterruptedException e )
		{
			System.out.println( "Erro" + e.getMessage( ) );
		}
		System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "
		        + c3.saldo + " C4: " + c4.saldo );
		System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );
	}
}

O que já tentei:

1) Sincronizar o bloco principal dentro de run(), da seguinte maneira:
Também tentei sem o sinchronized interno

public void run() {
		for (long i = 0; i < 1000000; i++) {
			synchronized (contas) {
				contas[0].debitar(1);
				contas[1].debitar(1);
				contas[2].debitar(1);

				synchronized (this) {
					this.creditar(3);
					this.transferir(contas[0], 1);
					this.transferir(contas[1], 1);
					this.transferir(contas[2], 1);
				}
			}
		}
	}

2) Sincronizando junto os metodos debitar, creditar e transferir.

3) Também tentei criar uma variável (membro da classe AgenteConta) para monitorar um lock e 2 metodos sincronizados, um para setar esta varivel como locked e outra para liberar, alterando também o métod run() conforme segue:

public synchronized void lock(){

		isLocked = true;
	}
	
		  
	public synchronized void unlock(){
		isLocked = false;
	    notify();
	}

	public void run() {
		lock();
		System.out.println(Thread.currentThread().getName());

		if (! isLocked) {
			for (long i = 0; i < 5; i++) {
				
				System.out.println(i);
				contas[0].debitar(1);
				contas[1].debitar(1);
				contas[2].debitar(1);
				this.creditar(3);
				this.transferir(contas[0], 1);
				this.transferir(contas[1], 1);
				this.transferir(contas[2], 1);
			}
			unlock();
		}
		else {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}
Tudo sem sucesso. :-/

34 Respostas

rogeriopaguilar

Bom, no código que você postou existem dois problemas a serem resolvidos. O primeiro está relacionado com o que é denominado como região crítica, ou seja, pensando em várias threads executando paralelamente, qual informação que pode ser alterada por mais de uma thread ao mesmo tempo? No caso que você mostrou é a variável saldo que será acessada pelas threads. Para resolver este tipo de problema, você deve sincronizar os locais do código que modificam essa variável e, desta forma, mesmo que acessada de forma concorrente, a variável será atualizada apenas por uma thread por vez. Para resolver este primeito problema, eu sugiro que você comente as chamadas ao método transferir que estão no método run e faça os testes apenas com as chamadas aos métodos debitar e creditar. Após corrigir este primeiro problema através das sincronizações de acesso necessárias, será necessário resolver o segundo problema, que ocorre com o método transferir. Para este método específico será necessário adquirir o lock nos dois objetos (o “this” e o passado como parâmetro) antes de executar as operações. Isto é necessário pois o método vai atualziar duas regiões críticas (os dois saldos), porém ocorrerá um deadlock e o programa vai ficar travado quando você rodar. Então para resolver isso primeiro você terá que entender o que é o deadlock e porque ele ocorre. Para evitar o deadlock, você terá que “simular” uma ordem nos locks dos dois objetos, já que essa ordem não pode ser garantida pelos parâmetros passados.
O capítulo 10 deste livro:

explica o problema que é encontrado relacionado aos deadlocks e a como devemos proceder para obter os locks na ordem correta. Nos capítulos anteriores (não me lembro exatamente qual agora) há a explicação sobre sincronização de variáveis em regiões críticas.
Eu arrumei o código aqui pra funcionar da forma que seu professor pediu. Se depois de tentar resolver ainda estiver muito complicado me manda uma mp que eu envio pra você. Espero ter ajudado. Até mais!

J

Rogerio,
Em primeiro lugar, obrigado pela ajuda, antes de te mandar uma mp e ver seu codigo, gostaria de continuar tentando entender onde estou errando, ou o que não estou enxergando, seguindo suas sugestões fiz alterações no código, sincronizando os métodos que fazem referencia ao saldo (aliás, eu ja tinha tentado isto), e também comentei as chamadas ao metodo transferir, conforme o codigo abaixo, mas contiuo sem sucesso.

public synchronized void debitar( double valor )
	{
		if ( valor <= saldo )
		{
			saldo -= valor;
		}
		else
		{
			System.out.println( "Saldo Insuficiente" );
		}
	}

	public synchronized void creditar( double valor )
	{
		saldo += valor;
	}

	public synchronized void transferir( AgenteConta c, double quantia )
	{
		c.saldo = c.saldo - quantia;
		this.saldo += quantia;
	}

	public void run() {
		for (long i = 0; i < 5; i++) {
			contas[0].debitar(1);
			contas[1].debitar(1);
			contas[2].debitar(1);

			this.creditar(3);

		//	this.transferir(contas[0], 1);
		//	this.transferir(contas[1], 1);
		//	this.transferir(contas[2], 1);

		}
	}

[color=darkblue]Sobre o conceito de travas explicitas? o que vc me diz? neste link http://www.cin.ufpe.br/~if686/aulas/14_Synchronized_Travas_PC.pdf por exemplo, na pagina 17 tem um conceito bem interessante mas bem diferente do que nos foi passado.[/color]

---

rogeriopaguilar

O que você fez está correto. Posta o código completo aqui, deveria estar funcionando sincronizando os métodos debitar e creditar e comentando as chamadas ao método transferir.
Com relação à nomenclatura eu não sei dizer qual é a correta. Quando tiver tempo eu vou ler o que você passou.

J

Mais uma vez obrigado pela disposição.

Aqui vai meu codigo completo (apenas mudei o saldo inicial para ficar mais fácil os testes):

public class AgenteConta implements Runnable
{
	private double saldo = 5.0;

	private AgenteConta[] contas = new AgenteConta[ 3 ];

	public AgenteConta( )
	{
	}

	public AgenteConta( AgenteConta c1, AgenteConta c2, AgenteConta c3 )
	{
		contas[ 0 ] = c1;
		contas[ 1 ] = c2;
		contas[ 2 ] = c3;
	}

	public synchronized void debitar( double valor )
	{
		if ( valor <= saldo )
		{
			saldo -= valor;
		}
		else
		{
			System.out.println( "Saldo Insuficiente" );
		}
	}

	public synchronized void creditar( double valor )
	{
		saldo += valor;
	}

	public synchronized void transferir( AgenteConta c, double quantia )
	{
			c.saldo = c.saldo - quantia;
			this.saldo += quantia;
	
	}

	public void run() {
		for (long i = 0; i < 5; i++) {
			contas[0].debitar(1);
			contas[1].debitar(1);
			contas[2].debitar(1);

			this.creditar(3);

			// this.transferir(contas[0], 1);
			// this.transferir(contas[1], 1);
			// this.transferir(contas[2], 1);

		}
	}

	public static void main( String[] args )
	{
		long t = System.currentTimeMillis( );
		AgenteConta c1 = new AgenteConta( );
		AgenteConta c2 = new AgenteConta( );
		AgenteConta c3 = new AgenteConta( );
		AgenteConta c4 = new AgenteConta( c1, c2, c3 );
		c1.contas = new AgenteConta[] { c2, c3, c4 };
		c2.contas = new AgenteConta[] { c1, c3, c4 };
		c3.contas = new AgenteConta[] { c1, c2, c4 };
		Thread t1 = new Thread( c1 );
		Thread t2 = new Thread( c2 );
		Thread t3 = new Thread( c3 );
		Thread t4 = new Thread( c4 );
		t1.start( );
		t2.start( );
		t3.start( );
		t4.start( );
		try
		{
			t1.join( );
			t2.join( );
			t3.join( );
			t4.join( );
		}
		catch ( InterruptedException e )
		{
			System.out.println( "Erro" + e.getMessage( ) );
		}
		System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "
		        + c3.saldo + " C4: " + c4.saldo );
		System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );
	}
}

[color=darkblue]Resultado de uma execução:[/color]
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
C1: 5.0 C2: 7.0 C3: 8.0 C4: 9.0
Tempo: 1

rogeriopaguilar

Volta o saldo para o valor do código original (1000000) e o looping para 1000000 também ao invés de 5 e testa de novo com os métodos sincronizados. Vai funcionar. Depois tira a sincronização e testa pra ver que dá errado (só pra vc ver como foi a sincronização que resolveu o problema).
Em outras palavras, não altere nenhum valor do código original, apenas sincronize os métodos como você já fez e comente as chamadas ao método transferir.

J

Ok,
Realmente “funciona”, mas alterei para um numero baixo por sugestão do próprio professor, fiz varios testes com varios números, a impressão que dá é que quanto maior o numero menor a probabilidade de erro, experiemente retirar apenas um zero e rode varias vezes o programa e vc vai ver que uma hora vai surgir inconsistencias, muuuito provavelmente se tivessemos paciencia de monitorar por um enooorrme tempo, apareceriam inconsistencias para qualquer número, não?

Para ser considerado sucesso o algoritmo teria que funcionar para qualquer número acima de 1.

rogeriopaguilar

Dependendo do valor do saldo que você colocar pode ser que o valor final mude sim, mas isso é difícil de testar, como você mesmo disse, pois estamos trabalhando com várias contas. No exemplo abaixo eu fiz algo um pouco mais simples pra entender. Existe apenas uma conta, e 10 threads executando as oprações incrementar e decrementar na mesma instância da conta. Você pode ver que no método run eu coloquei um sleep. Eu fiz isso de propósito, pois senão o loop ia ficar tão rápido que, dependendo da velocidade do processador, mesmo executando em várias threads, o efeito do paralelismo não seria notado porque cada trhead conseguiria terminar todas as operações no seu tempo de execução. No método run eu estou incrementando 1 cinco vezes e decrementando 5, ou seja, independente do número de threads executando, no final o saldo deve ser o mesmo que tínhamos no início. Teste o primeiro código, sem sincronização. Depois teste o segundo e você verá que o saldo será sempre igual ao inicial, devido à sincronização. Acho que somente com um objeto compartilhado fica mais fácil de entender do que com três:

Primeiro teste (vai dar errado):

public class Conta {

	private double saldo = 100;
	
	public  void incrementar(double valor) {
		this.saldo = this.saldo + valor;
	}
	
	public  void decrementar(double valor){
		this.saldo = this.saldo - valor;
	}

	public static void main(String[] args) {
		System.out.println("Começo do teste...");
		for(int i = 0; i < 10; i++, teste());
		System.out.println("Final do teste...");
	}	
	public static void teste() {
		final Conta c = new Conta();
		
		Runnable r = new Runnable() {
			
			@Override
			public void run() {
				for(long i = 0; i < 100; i++ ) {
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					try{Thread.sleep(20);}catch(Exception e){e.printStackTrace();}
					c.decrementar(5);
				}
			}
		};

			Thread t1 = new Thread(r);
			Thread t2 = new Thread(r);
			Thread t3 = new Thread(r);
			Thread t4 = new Thread(r);
			Thread t5 = new Thread(r);
			Thread t6 = new Thread(r);
			Thread t7 = new Thread(r);
			Thread t8 = new Thread(r);
			Thread t9 = new Thread(r);
			Thread t10 = new Thread(r);
			
			t1.start();
			t2.start();
			t3.start();
			t4.start();
			t5.start();
			t6.start();
			t7.start();
			t8.start();
			t9.start();
			t10.start();

			try{
				t1.join();
				t2.join();
				t3.join();
				t4.join();
				t5.join();
				t6.join();
				t7.join();
				t8.join();
				t9.join();
				t10.join();
			}catch(Exception e){e.printStackTrace();}
			
			System.out.println("Saldo esperado: 100     Saldo obtido: " + c.saldo);
		}
		
	}

Segundo teste (vai dar certo devido ao synchronized nos métodos):

public class Conta {

	private double saldo = 100;
	
	public  synchronized void incrementar(double valor) {
		this.saldo = this.saldo + valor;
	}
	
	public  synchronized void decrementar(double valor){
		this.saldo = this.saldo - valor;
	}

	public static void main(String[] args) {
		System.out.println("Começo do teste...");
		for(int i = 0; i < 10; i++, teste());
		System.out.println("Final do teste...");
	}	
	public static void teste() {
		final Conta c = new Conta();
		
		Runnable r = new Runnable() {
			
			@Override
			public void run() {
				for(long i = 0; i < 100; i++ ) {
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					c.incrementar(1);
					try{Thread.sleep(20);}catch(Exception e){e.printStackTrace();}
					c.decrementar(5);
				}
			}
		};

			Thread t1 = new Thread(r);
			Thread t2 = new Thread(r);
			Thread t3 = new Thread(r);
			Thread t4 = new Thread(r);
			Thread t5 = new Thread(r);
			Thread t6 = new Thread(r);
			Thread t7 = new Thread(r);
			Thread t8 = new Thread(r);
			Thread t9 = new Thread(r);
			Thread t10 = new Thread(r);
			
			t1.start();
			t2.start();
			t3.start();
			t4.start();
			t5.start();
			t6.start();
			t7.start();
			t8.start();
			t9.start();
			t10.start();

			try{
				t1.join();
				t2.join();
				t3.join();
				t4.join();
				t5.join();
				t6.join();
				t7.join();
				t8.join();
				t9.join();
				t10.join();
			}catch(Exception e){e.printStackTrace();}
			
			System.out.println("Saldo esperado: 100     Saldo obtido: " + c.saldo);
		}
		
	}
J

Rogerio,
Ai é que está, somente com 1 objeto compartilhado fizemos varios exercicios e pararece não restar mais dúvidas para ninguem da turma, mas o desafio é para este problema específico, depois que postei a ultima msg, fiz varios testes e, mesmo com o numero original (10000000) acontece a inconsistencia varias vezes.

Como eu disse, o desafio é resolver o exercicio como apresentado, o professor diz que tem solução mas não a apresenta.

Há quem aposte que é insolúvel, sei lá, tá estranho.

rogeriopaguilar

Bom, eu acho que o desafio que ele quer que vcs resolvam é na verdade o que vocês tem que fazer no método transferir, que é diferente da sincronização normal devido ao problema do deadlock que vai acontecer, e não nos outros dois métodos. No caso dos métodos acrescentar e decrementar o que precisa ser feito é só sincronizar mesmo. Eu testei aqui com os valores originais e não deu problema nenhuma vez (o valor original era um milhão e não dez milhões). Eu não sei que valores você está colocando para o saldo e para o contador do looping para dar errado. Me fala um caso pra eu testar aqui.

G

o problema é o debitar.
é o único método q sofre o efeito de um saldo baixo.

mude ele pra ficar assim:
public synchronized void debitar( double valor )  
    {  
		// if ( valor <= saldo )  
		// {  
			 saldo -= valor;  
		// }  
		// else  
		// {  
		//	 System.out.println( "Saldo Insuficiente" );  
		// }  
    }
coloque qq valor agora no saldo.

pode ser até 0.00

J

@Rogério,

O desafio é que o algoritmo rode com qualquer valor, inclusive a sugestão é que iniciemos com o valor 5, segui sua sugestão e comentei as chamadas ao metodo transferir, e entao testei com varios valores, valores baixos com o 5 ja dão erro “de cara”, valores muito altos podem demorar mas uma hora ocorre o erro.

De qualquer forma, o principal desafio é que funcione com qualquer valor acima de 1, com suas sugestoes, mesmo desconsiderando o metodo transferir (que seria o “gargalo”) nao obitve sucesso com valores baixos.

@Gilson,

Vou testar sua sugestão

rogeriopaguilar

Se vc comentar como o Gilson disse vai funcionar com qualquer saldo, porém você tem que sincronizar os métodos. Se não sincronizar não adianta comentar ou não aquele ponto. Então a solução final é deixar ele fazer o saldo ficar negativo e sincronizar os métodos. E não esqueça de deixar as chamadas ao método transferir comentadas também como eu falei antes pra você testar essa primeira parte, senão o valor vai sair errado.
Depois que descomentar as chamadas ao método transferir tem que arrumar ele també, só que é um pouco mais complicado que esta primeira parte.

G

rogeriopaguilar:
Se vc comentar como o Gilson disse vai funcionar com qualquer saldo, porém você tem que sincronizar os métodos, comentando ali ou não.
Então a solução final é deixar ele fazer o saldo ficar negativo e sincronizar os métodos.
Falta agora o método transferir, que é um pouco mais complicado.

creio q o transferir funciona normal pq ele não se condiciona

rogeriopaguilar

Bom, nos casos que testei aqui não funcionou. Eu acredito que o professor quer mostrar como resolver um deadlock com este método.

J

Rogerio,
Neste momento não importa a resolução do deadlock, o fato é que mesmo comentando o metodo transferir não esta funcionando, entao só vou pensar em resolver o DL depois que conseguir fazer funcionar sem o metodo transferir, se não nem vale o esforço, aliás, cheguei nesta conculsão a partir de uma sugestão sua.

Não adiantaria resolver o DL e o resultado continuar saindo inconsistente.

Gilson,
Verdade assim funciona mas deste jeito estou quebrando uma regra que nao poderia, pois posso até acrescentar coisas mas retirar coisas dos metodos.

rogeriopaguilar

A sincronização para os métodos adicionar e decrementar está correta. Se quiser coloca o enunciado aqui pra eu entender quando você diz que não está correto, queria entender o caso que não está dando certo.

G

tente assim:

fiz aki mts testes e tds ok.

public class AgenteConta implements Runnable  
{  
    private double saldo = 1000000.0;
    
    private AgenteConta[] contas = new AgenteConta[ 3 ];  
  
    public AgenteConta() {
    }

    public AgenteConta(AgenteConta c1, AgenteConta c2, AgenteConta c3)  
    {  
        contas[ 0 ] = c1;  
        contas[ 1 ] = c2;  
        contas[ 2 ] = c3;  
    }  
  
    private void incSaldo(double v){
        synchronized (this) {			
            saldo = saldo + v;
        }
    }
	
    public void debitar( double valor )  
    {  
        if ( valor <= saldo )  {  
            incSaldo(- valor);  
        } else {  
            System.out.println( "Saldo Insuficiente" );  
        }  
    }  
  
    public void creditar( double valor )  
    {  
        incSaldo(valor);  
    }  
  
    public void transferir( AgenteConta c, double quantia )  
    {  
        c.incSaldo(- quantia);  
        this.incSaldo(quantia);
    }  
  
    public void run() { 
        for (long i = 0; i < 1000000; i++) {  
            contas[0].debitar(1);  
            contas[1].debitar(1);  
            contas[2].debitar(1);  

            this.creditar(3);  

             this.transferir(contas[0], 1);  
             this.transferir(contas[1], 1);  
             this.transferir(contas[2], 1);
        }
    }  
  
    public static void main( String[] args )  
    {  
        long t = System.currentTimeMillis( );  
        AgenteConta c1 = new AgenteConta();  
        AgenteConta c2 = new AgenteConta();  
        AgenteConta c3 = new AgenteConta();  
        AgenteConta c4 = new AgenteConta( c1, c2, c3);  
        c1.contas = new AgenteConta[] { c2, c3, c4 };  
        c2.contas = new AgenteConta[] { c1, c3, c4 };  
        c3.contas = new AgenteConta[] { c1, c2, c4 };  
        Thread t1 = new Thread( c1 );  
        Thread t2 = new Thread( c2 );  
        Thread t3 = new Thread( c3 );  
        Thread t4 = new Thread( c4 );  

        t1.start( );
        t2.start( );  
        t3.start( );  
        t4.start( );  

        try  
        {  
            t1.join( );  
            t2.join( );  
            t3.join( );  
            t4.join( );  
        }  
        catch ( InterruptedException e )  
        {  
            System.out.println( "Erro" + e.getMessage( ) );  
        }  
        System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "  
                + c3.saldo + " C4: " + c4.saldo );  
  
        System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );  
    }  
}
G

poderia ser assim tb.

public class AgenteConta implements Runnable    
{    
    private double saldo = 1000000.0;  
      
    private AgenteConta[] contas = new AgenteConta[ 3 ];    
    
    public AgenteConta() {  
    }  
  
    public AgenteConta(AgenteConta c1, AgenteConta c2, AgenteConta c3)    
    {    
        contas[ 0 ] = c1;    
        contas[ 1 ] = c2;    
        contas[ 2 ] = c3;    
    }    
    
    public void debitar( double valor )    
    {    
        synchronized (this) {             
		    if ( valor <= saldo )  {    
		        saldo -= valor;    
		    } else {    
		        System.out.println( "Saldo Insuficiente" );    
		    }   
        }
    }    
    
    public void creditar( double valor )    
    {    
        synchronized (this) {             
        	saldo += valor;
        }
    }    
    
    public void transferir( AgenteConta c, double quantia )    
    {    
        synchronized (c) {             
        	c.saldo -= quantia;
        }
        
        synchronized (this) {             
        	this.saldo += quantia;
        }	
    }    
    
    public void run() {   
        for (long i = 0; i < 1000000; i++) {    
            contas[0].debitar(1);    
            contas[1].debitar(1);    
            contas[2].debitar(1);    
  
            this.creditar(3);    
  
             this.transferir(contas[0], 1);    
             this.transferir(contas[1], 1);    
             this.transferir(contas[2], 1);  
        }  
    }    
    
    public static void main( String[] args )    
    {    
        long t = System.currentTimeMillis( );    
        AgenteConta c1 = new AgenteConta();    
        AgenteConta c2 = new AgenteConta();    
        AgenteConta c3 = new AgenteConta();    
        AgenteConta c4 = new AgenteConta( c1, c2, c3);    
        c1.contas = new AgenteConta[] { c2, c3, c4 };    
        c2.contas = new AgenteConta[] { c1, c3, c4 };    
        c3.contas = new AgenteConta[] { c1, c2, c4 };    
        Thread t1 = new Thread( c1 );    
        Thread t2 = new Thread( c2 );    
        Thread t3 = new Thread( c3 );    
        Thread t4 = new Thread( c4 );    
  
        t1.start( );  
        t2.start( );    
        t3.start( );    
        t4.start( );    
  
        try    
        {    
            t1.join( );    
            t2.join( );    
            t3.join( );    
            t4.join( );    
        }    
        catch ( InterruptedException e )    
        {    
            System.out.println( "Erro" + e.getMessage( ) );    
        }    
        System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "    
                + c3.saldo + " C4: " + c4.saldo );    
    
        System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );    
    }    
}
rogeriopaguilar

A solução que eu cheguei foi a seguinte:

package teste;

public class AgenteContaFinal implements Runnable
{
	private double saldo = 2000000.0;

	private AgenteContaFinal[] contas = new AgenteContaFinal[ 3 ];
	
	public AgenteContaFinal( )
	{
	}

	public AgenteContaFinal( AgenteContaFinal c1, AgenteContaFinal c2, AgenteContaFinal c3 )
	{
		contas[ 0 ] = c1;
		contas[ 1 ] = c2;
		contas[ 2 ] = c3;
	}

	public synchronized void debitar( double valor )
	{
		if ( valor <= saldo )
		{
			saldo -= valor;
		}
		else
		{
			System.out.println( "Saldo Insuficiente" );
		}
	}

	public synchronized void creditar( double valor )
	{
		saldo += valor;
	}

	public void transferir( AgenteContaFinal c, double quantia )
	{
		int hash = System.identityHashCode(this);
		int hashOutroObjeto = System.identityHashCode(c);
		Object lockA = null;
		Object lockB = null;
		if(hash < hashOutroObjeto) {
			lockA = this;
			lockB = c;
		} else {
			lockA = c;
			lockB = this;
		}
		
		synchronized(lockA) {
			synchronized (lockB) {
				if ( quantia <= c.saldo ) {
					c.saldo = c.saldo - quantia;
					this.saldo += quantia;
				} else {
					System.out.println("Saldo insuficiente");
				}
			}
		}
		
	}

	public void run( )
	{
		for ( long i = 0; i < 2000000; i++ )
		{
			contas[ 0 ].debitar( 1 );
			contas[ 1 ].debitar( 1 );
			contas[ 2 ].debitar( 1 );
			this.creditar( 3 );
			this.transferir( contas[ 0 ], 1 );
			this.transferir( contas[ 1 ], 1 );
			this.transferir( contas[ 2 ], 1 );
		}
	}

	public static void main( String[] args )
	{
		
	   long t = System.currentTimeMillis( );  
        AgenteContaFinal c1 = new AgenteContaFinal( );  
        AgenteContaFinal c2 = new AgenteContaFinal( );  
        AgenteContaFinal c3 = new AgenteContaFinal( );  
        AgenteContaFinal c4 = new AgenteContaFinal( c1, c2, c3 );  
        c1.contas = new AgenteContaFinal[] { c2, c3, c4 };  
        c2.contas = new AgenteContaFinal[] { c1, c3, c4 };  
        c3.contas = new AgenteContaFinal[] { c1, c2, c4 };  
        Thread t1 = new Thread( c1 );  
        Thread t2 = new Thread( c2 );  
        Thread t3 = new Thread( c3 );  
        Thread t4 = new Thread( c4 );  
        t1.start( );  
        t2.start( );  
        t3.start( );  
        t4.start( );  
		try
		{
			t1.join( );
			t2.join( );
			t3.join( );
			t4.join( );
		}
		catch ( InterruptedException e )
		{
			System.out.println( "Erro" + e.getMessage( ) );
		}
        
		System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "
		        + c3.saldo + " C4: " + c4.saldo );
		System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );
	}
}

A diferença está na sincronização do método transferir, pois eu fiz o lock nas duas regiões críticas (a da conta corrente atual e da outra) antes de atualizar elas, só que tem que garantir a ordem do lock independente dos objetos passados como parâmetro, para evitar problemas de deadlock. Você tem que adquirir o lock das duas referências, e não de uma delas por vez. Pra entender o porque eu é necessário ler o item 10.1.2 do livro que eu falei lá no começo. Pra entender porque os outros métodos precisam de sincronização e sobre sincronização em geral, é bom ler os capítulos 2 e 3 do mesmo livro.

J

Rogério,
Fiz um teste com esta sua ultima versão, colocando 5.0 no saldo inicial e no loop, novamente nao funcionou, como eu disse antes, para ser considerado ok, precisa funcionar com qualquer saldo inicial, veja a saida do meu testes:

Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
C1: 5.0 C2: 11.0 C3: 13.0 C4: 9.0
Tempo: 1

J

GilsonNunes:
tente assim:
fiz aki mts testes e tds ok.

Gilson, fiz vários testes e no inicio realmente parece ok, mas se vc insisitir vai se deparar com inconsistencias, coloquei 5 no loop e no saldo inicial, veja a saida do meu ultimo teste:

Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
C1: 5.0 C2: 13.0 C3: 12.0 C4: 7.0
Tempo: 1

rogeriopaguilar

Como o Gilson falou antes, pra funcionar sempre com qualquer saldo tem que tirar a condição de verificação para valores negativos. Só porque com 5 no saldo o resultado final não foi 5 no final não significa que está errado. Executar várias vezes com o mesmo saldo e obter dados diferentes também não quer dizer que está errado porque com threads você não tem controle sobre a ordem das operações. Dependendo da orgem de execução das operações das threads em alguns momentos o saldo não vai ser suficiente mesmo. Você até poderia fazer a thread esperar no método debitar até que o valor fosse suficiente para atender o débito, mas como você não tem controle sobre a ordem das operações quando utiliza threads pode ser que o valor nunca seja suficiente para atender aquela solicitação e a thread fique “esperando para sempre”.

clonesnoop

Bom dia galera.

Primeiro deixe-me dizer que não sou expert em JAVA, e muito menos gosto de JAVA.
Meu negócio é C/C++

Mas conceitos são conceitos. Então... Vamos lá

Modifique a implementação da classe AgenteConta utilizando somente travas explicitas de modo que, ao final da execução, suas quatro instâncias tenham o mesmo saldo (1.000.000,00). É proibido modifcar o método main() e é proibido também usar trava ou monitor global único para todas as threads. Também é probibido mudar a lógica da aplicação ([color=red]i.e., cada instancia de AgenteConta deve continuar debitando das outras contas, creditando para si e, em seguida, transferindo uma unidade para cada uma das outras contas[/color]) e a sequência de operações realizadas no método run().

Leia direito a parte grifada do enunciado do exercício. Você notará que não condiz com o código do método transferir:

public void transferir( AgenteConta c, double quantia )  
{
    c.saldo = c.saldo - quantia;  
    this.saldo += quantia;  
}

Você deve tirar 'quantia' da conta atual e somar essa 'quantia' para conta 'c', como abaixo:

public void transferir( AgenteConta c, double quantia )  
{
    c.saldo = c.saldo + quantia;  
    this.saldo -= quantia;  
}

Logo, equivale a:

public void transferir( AgenteConta c, double quantia )  
{
    c.debitar(quantia);  
    this.creditar(quantia);  
}

Observe que fazendo isso e não colocando trava alguma(synchronized,lock e etc), a probabilidade da mensagem "Saldo Insuficiente" aparecer é muitissimo pequena.
Execute, teste e vc verá.

Agora existem varias maneiras de se resolver:

Vamos explorar os monitores globais, encontrei duas formas:

vc pode colocar um monitor global estático(da classe) no método 'run', veja abaixo:

public  void run() {
		for (long i = 0; i &lt; 1000000; i++) {
			synchronized (AgenteConta.class) {
				
				contas[0].debitar(1);
				contas[1].debitar(1);
				contas[2].debitar(1);

				this.creditar(3);
				this.transferir(contas[0], 1);
				this.transferir(contas[1], 1);
				this.transferir(contas[2], 1);
			}
		}
}

OU, usando um monitor global nos métodos 'debitar' e 'creditar' com uma instância de 'AgenteConta', o único detalhe é que vc precisa analisar qual instância as Threads têm em comum, como abaixo:

public void creditar(double valor) {
		synchronized (contas[1].contas[2]) {
			saldo+=valor;
		}
}

public void debitar(double valor) {
		synchronized(contas[1].contas[2]){
			if(valor&gt;saldo){
				System.out.println(&quot;Saldo insuficiente&quot;);
			}
			else{
				saldo-=valor;
			}
		}
}

Note que fazendo isso o tempo de execução aumenta conforme aumenta-se o valor do saldo.(Obs: teste feito com 1000000,1000,50 e 5)

Ou, Usando a suposta "trava explicita", sem monitor global, alterando os métodos 'debitar' e 'creditar':

public synchronized void debitar(double valor) {
			while(saldo&lt;=0){
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(valor&gt;saldo){
				System.out.println(&quot;Saldo insuficiente&quot;);
			}
			else{
				saldo-=valor;
			}
			notifyAll();
}

public synchronized void creditar(double valor) {
	saldo+=valor;
	notifyAll();
}

Muito bem, a listagem completa pro seu problema obedecendo as restrições impostas, fica assim:

public class AgenteConta implements Runnable {
	private double saldo = 1000000;
	private AgenteConta[] contas = new AgenteConta[3];

	public AgenteConta(AgenteConta c1, AgenteConta c2, AgenteConta c3) {
		contas[0] = c1;
		contas[1] = c2;
		contas[2] = c3;

	}

	public AgenteConta() {

	}

	public synchronized void debitar(double valor) {
		while (saldo &lt;= 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (valor &gt; saldo) {
			System.out.println(&quot;Saldo insuficiente&quot;);
		} else {
			saldo -= valor;
		}
		notifyAll();
	}

	public synchronized void creditar(double valor) {
		saldo += valor;
		notifyAll();
	}

	public void transferir(AgenteConta c, double quantia) {
		c.creditar(quantia);
		this.debitar(quantia);
	}

	public void run() {
		for (long i = 0; i &lt; 1000000; i++) {

			contas[0].debitar(1);
			contas[1].debitar(1);
			contas[2].debitar(1);

			this.creditar(3);
			this.transferir(contas[0], 1);
			this.transferir(contas[1], 1);
			this.transferir(contas[2], 1);
		}

	}

	/**
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		long t = System.currentTimeMillis();
		AgenteConta c1 = new AgenteConta();
		AgenteConta c2 = new AgenteConta();
		AgenteConta c3 = new AgenteConta();
		AgenteConta c4 = new AgenteConta(c1, c2, c3);
		c1.contas = new AgenteConta[] { c2, c3, c4 };
		c2.contas = new AgenteConta[] { c1, c3, c4 };
		c3.contas = new AgenteConta[] { c1, c2, c4 };
		Thread t1 = new Thread(c1);

		Thread t2 = new Thread(c2);

		Thread t3 = new Thread(c3);

		Thread t4 = new Thread(c4);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

		try {
			t1.join();
			t2.join();
			t3.join();
			t4.join();
		} catch (InterruptedException e) {
			System.out.println(&quot;Erro&quot; + e.getMessage());
		}
		System.out.println(&quot;C1: &quot; + c1.saldo + &quot; C2: &quot; + c2.saldo + &quot; C3: &quot;
				+ c3.saldo + &quot; C4: &quot; + c4.saldo);
		System.out.println(&quot; Tempo: &quot; + (System.currentTimeMillis() - t));
	}

}

Valeu Galera é isso aí. Qualquer coisa que eu tiver dito errado, podem ficar a vontade para postar uma correção.

rogeriopaguilar

Pelo que vi no enunciado parece que precisa mudar o sinal das operações no método de transferência, ele está fazendo o contrário do que está escrito na questão.
Bom, então na minha implementação é só mudar a ordem no método transferir. E eu mudei também o método run pra fazer vários testes em sequência.

package teste;


public class AgenteContaFinal implements Runnable
{
	private double saldo = 5;
	private long qtde;
	
	private AgenteContaFinal[] contas = new AgenteContaFinal[ 3 ];
	
	public AgenteContaFinal( )
	{
	}

	public AgenteContaFinal( AgenteContaFinal c1, AgenteContaFinal c2, AgenteContaFinal c3 )
	{
		contas[ 0 ] = c1;
		contas[ 1 ] = c2;
		contas[ 2 ] = c3;
	}

	public synchronized void debitar( double valor )
	{
		if( valor <= saldo ) {
			saldo -= valor;
		} else {
			System.out.println("Insuficiente");
		}
	}

	public synchronized void creditar( double valor )
	{
		saldo += valor;
	}

	public void transferir( AgenteContaFinal c, double quantia )
	{
		int hash = System.identityHashCode(this);
		int hashOutroObjeto = System.identityHashCode(c);
		Object lockA = null;
		Object lockB = null;
		if(hash < hashOutroObjeto) {
			lockA = this;
			lockB = c;
		} else {
			lockA = c;
			lockB = this;
		}
		
		synchronized(lockA) {
			synchronized (lockB) {
				if ( quantia <= saldo ) {
					c.saldo = c.saldo + quantia;
					this.saldo -= quantia;
				} else {
					System.out.println("Saldo insuficiente");
				}
			}
		}
		
	}

	public void run( )
	{
		for ( long i = 0; i < qtde; i++ )
		{
			contas[ 0 ].debitar( 1 );
			contas[ 1 ].debitar( 1 );
			contas[ 2 ].debitar( 1 );
			this.creditar( 3 );
			this.transferir( contas[ 0 ], 1 );
			this.transferir( contas[ 1 ], 1 );
			this.transferir( contas[ 2 ], 1 );
		}
	}

	public static void main( String[] args )
	{
		
    	for(long q = 5; q < 100; q++) {
	        long t = System.currentTimeMillis( );  
	        AgenteContaFinal c1 = new AgenteContaFinal();  
	        AgenteContaFinal c2 = new AgenteContaFinal();  
	        AgenteContaFinal c3 = new AgenteContaFinal();  
	        AgenteContaFinal c4 = new AgenteContaFinal( c1, c2, c3);  
	        c1.qtde=q;
	        c2.qtde=q;
	        c3.qtde=q;
	        c4.qtde=q;
	        c1.saldo=q;
	        c2.saldo=q;
	        c3.saldo=q;
	        c4.saldo=q;
	        
	        c1.contas = new AgenteContaFinal[] { c2, c3, c4 };  
	        c2.contas = new AgenteContaFinal[] { c1, c3, c4 };  
	        c3.contas = new AgenteContaFinal[] { c1, c2, c4 };  
	        Thread t1 = new Thread( c1 );  
	        Thread t2 = new Thread( c2 );  
	        Thread t3 = new Thread( c3 );  
	        Thread t4 = new Thread( c4 );  
	
	        t1.start( );
	        t2.start( );  
	        t3.start( );  
	        t4.start( );  
	
	        try  
	        {  
	            t1.join( );  
	            t2.join( );  
	            t3.join( );  
	            t4.join( );  
	        }  
	        catch ( InterruptedException e )  
	        {  
	            System.out.println( "Erro" + e.getMessage( ) );  
	        }  
	        System.out.println( " C1: " + c1.saldo + " C2: " + c2.saldo + " C3: "  
	                + c3.saldo + " C4: " + c4.saldo );  
	        
	        Double saldoS1 = c1.saldo;
	        Double saldoS2 = c2.saldo;
	        Double saldoS3 = c3.saldo;
	        Double saldoS4 = c4.saldo;
	        if( saldoS1.compareTo(saldoS2) != 0 ||
	            saldoS1.compareTo(saldoS3) != 0 ||
	            saldoS1.compareTo(saldoS4) != 0 ||	        		
	            
		        saldoS2.compareTo(saldoS1) != 0 ||
			    saldoS2.compareTo(saldoS3) != 0 ||
			    saldoS2.compareTo(saldoS4) != 0 ||	        		

		        saldoS3.compareTo(saldoS1) != 0 ||
			    saldoS3.compareTo(saldoS2) != 0 ||
			    saldoS3.compareTo(saldoS4) != 0 ||	        		

		        saldoS4.compareTo(saldoS1) != 0 ||
			    saldoS4.compareTo(saldoS2) != 0 ||
			    saldoS4.compareTo(saldoS3) != 0
	        ) {
	        	throw new RuntimeException("Ocorreu algum problema na ordem de execução das operações!");
	        }
	        	
	        System.out.println( " Tempo: " + (System.currentTimeMillis( ) - t) );  
    	}
		
		
	}
}
rogeriopaguilar

Agora que estou lendo as outras mensagens vi que já tinha percebido o que eu falei :slight_smile:
Sacanagem desse professor, kkk

Eu fiquei com uma dúvida. No método debitar você espera o saldo ficar maior que zero. Neste exemplo específico não tem problema porque da forma que está o looping sempre alguma thread vai creditar em algum momento o valor necessário para o débito. Mas num código para qualquer quantia de créditos/débitos, não deveríamos apenas informar o usuário que não tem saldo para o débito e retornar, para evitar que a thread fique “travada” neste ponto, ou seja, esperando um crédito para realizar o débito e esse crédito talvez nunca ocorra?

clonesnoop

Agora que estou lendo as outras mensagens vi que já tinha percebido o que eu falei :slight_smile:
Sacanagem desse professor, kkk

Eu fiquei com uma dúvida. No método debitar você espera o saldo ficar maior que zero. Neste exemplo específico não tem problema porque da forma que está o looping sempre alguma thread vai creditar em algum momento o valor necessário para o débito. Mas num código para qualquer quantia de créditos/débitos, não deveríamos apenas informar o usuário que não tem saldo para o débito e retornar, para evitar que a thread fique “travada” neste ponto, ou seja, esperando um crédito para realizar o débito e esse crédito talvez nunca ocorra?

Muito bem Rogerio. O que vc disse está certo, pela estrutura desse exercício em algum momento as threads terão crédito adicionado ao saldo. Fazendo com que a brincadeira continue até o fim do ‘for’.

Para o Pessoal que ainda não entendeu, experimente comentar a linha ‘this.creditar(3);’ do método ‘run()’, e rode o programa.
Vcs perceberão que entrará num deadlock.

Rogerio pelo que entendi na sua pergunta vc quer simular uma situação em que pode-se creditar/debitar qualquer quantia(sendo elas diferentes: quantia_debitar != quantia_creditar).

Se vc fizer isso a chance de ocorrer deadlock é quase sempre certa. Então para evitar esse deadlock vc pode deixar o método ‘debitar’ assim:

public synchronized void debitar(double valor) { if (saldo < valor) { System.out.println("Saldo insuficiente"); return; //Como o Rogerio sugeriu } saldo -= valor; }

descomente a linha ‘this.creditar(3);’ do método ‘run()’, retire todos os ‘notifyAll()’ do programa inteiro e faça um teste.
A msg “Saldo insuficiente” não deverá aparecer. certo?
Agora comente novamente e torne a executar.
A msg “saldo insuficiente” deve aparecer e o programa chegar ao fim, porém os saldos finais não são iguais.

Agora vamos entrar nos detalhes:

Usando o ‘return’ no método ‘debitar’ vc não impede que a thread continue executando, ou seja ela continua disputando ciclos de cpu com as outras threads.
Com o ‘wait()’ eu tiro a thread da fila ‘pronto’ do S.O. e coloco na fila ‘espera’. Sendo a mesma retirada desta fila quando uso o ‘notifyAll()’.
Assim eu disponibilizo ciclos de cpu para outros processos executando no meu S.O.
Mas isso só funcionará neste caso. Como dizem “cada caso é um caso”, cabe a nós analisarmos a situação.

Acho esse exercício meramente didático, na vida real é quase improvável ter um caso destes.(posso estar errado :roll: )

Conclusão:
Nesse caso o objetivo é que ao terminar o programa todas as threads tenham o mesmo valor de saldo. Portanto para garantir que as threads cheguem ao fim do programa com saldos iguais, eu uso o ‘wait’ ao invés de ‘return’, assim se uma delas não tiver nos conformes o programa não termina.(Claro que isso só acontecerá se alterarmos a lógica do programa: método run(),main() e etc);
Oia… vale dois certos, não acham?!

Bom, acho que respondi a pergunta. Se não era isso que vc queria saber, por favor tente ser mais claro.

Grato Pessoal.

rogeriopaguilar

Era isso mesmo. Valeu pela resposta. No caso do deadlock quando estava fazendo aqui ele ocorreu quando tentei sincronizar A e logo depois B no método transferir, por isso tive que mudar o código para garantir a ordem dos locks nos objetos, já que essa é uma das maneiras de se evitar o deadlock. Agora eu não sei direito qual é o objetivo do professor, se é ensinar como sincronizar o código para regiões críticas apenas, se é para sincronizar e evitar que deadlocks ocorram entre threads que competem pelo acesso a estas regiões críticas, ou se era apenas para resolver a pegadinha do malandro no enunciado :slight_smile:

J

Fiz um teste com esta sua “listagem completa”, colocando 5 no saldo inicial e no loop, não funfa! resultado:

Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
Saldo Insuficiente
C1: 5.0 C2: 11.0 C3: 8.0 C4: 17.0
Tempo: 2

G

eu acho q tem q funcionar com 1 milhão.

e o exemplo q postei funciona com 1 milhao sem problema.

J

Não, tem que funcionar com qualquer valor acima de 1.

G

Não, tem que funcionar com qualquer valor acima de 1.

se vc colocar 2.

a primeira thread dispara e antes q vc da o start na segunda

já vai dar saldo insuficiente. pq ela só debita nas outras.

J

Não, tem que funcionar com qualquer valor acima de 1.

se vc colocar 2.

a primeira thread dispara e antes q vc da o start na segunda

já vai dar saldo insuficiente. pq ela só debita nas outras.

Então Gilson,
Acho que a unica maneira de conseguir fazer com que funcione com qualquer número seria uma forma de travar por thread, ou seja, se uma thread entrar no run, ela teria que executar todo o codigo da classe antes de liberar para outra thread, agora… Como conseguir este tipo de “trava”?

G

mas ia q não iria funcionar mesmo.

imagine;

a primeira debitaria 1 milha de vezes na outra.

da pra testar facil

t1.start;

t1.join;

t2.start;

t2.join;




J

GilsonNunes:
mas ia q não iria funcionar mesmo.

imagine;

a primeira debitaria 1 milha de vezes na outra.

da pra testar facil

t1.start;

t1.join;

t2.start;

t2.join;



</blockquote>

Não, nao foi isto que eu quis dizer, imagine que a primeira thread entrasse e fizesse o “trabalho dela” e saisse, depois uma outra e assim sucessivamente, ou seja, algo do tipo atomico, uma thread teria que executar todo o codigo para dar a vez para outra, mas mesmo assim não sei se funcionaria, pois um mesma thread poderia ganhar a “disputa” por mais vezes cque as outras e no fim ocorreriam inconsistencias de qualquer forma, se garantissemos que cada thread executasse de forma atomica o codigo e em uma sequencia pre-determinada, do tipo: t1, t2, t3, t4, acho atenderia, mas ai não vejo vantagem do uso de thread, sei lá… isto tá me parecendo “pegadinha”. :slight_smile:

Criado 12 de abril de 2012
Ultima resposta 16 de abr. de 2012
Respostas 34
Participantes 4