Jantar dos filósofos com synchronized

4 respostas
D

Problema do jantar dos filósofos

Cinco filósofos tem um prato de espaguete. O espaguete está tão escorregadio que um filósofo precisa de dois garfos para comê-lo. Entre cada par de pratos está um garfo.
A vida de um filósofo consiste em alternar período de comer e pensar. Quando uma filósofa fica fome, ela tenta pegar os garfos à sua direita e à sua esquerda, um de cada vez em qualquer ordem. Se conseguir pegar dois garfos, ela comerá durante um determinado tempo e, então, colocará os garfos na mesa novamente e continuará a pensar. A questão fundamental é: você consegue escrever um programa para cada filósofo que faça o que deve fazer e nunca trave?
Tanenbaum, Andrew S.

Bom têm que resolver o problema de duas formas, a primeira é essa:
[já funcionando]

package jantarFilosofico;

public class Garfo {
	
		private int idGarfo;
		private boolean estadoGarfo;
		private int dono;
		
		public Garfo(int id){
			idGarfo = id;
			estadoGarfo = false; // desocupado		
			dono = -1; // sem dono
		}
		
		public int getIdGarfo(){
			return idGarfo;
		}
		
		public void setIdGarfo(int g){
			idGarfo = g;
		}
		
		public int getDonoGarfo(){
			return dono;
		}
		
		public void setDonoGarfo(int d){
			dono = d;	
		}
		
		public boolean getEstadoGarfo(){
			return estadoGarfo;
		}
		
		public void setEstadoGarfo(boolean ocupado){
			estadoGarfo = ocupado;
		}
}
package jantarFilosofico;

import java.util.List;
import jantarFilosofico.Garfo;

class Filosofo implements Runnable { 
	final int N = 5; // são cinco filosofos e cinco garfos...
	List <Garfo> garfos; // garfos disponíveis 0, 1, 2, 3 e 4
	int filosofo;
	Filosofo (List <Garfo>gs, int fil){
		garfos = gs;
		filosofo = fil;		
	}
	
	public void run(){
		for (int i=0; i<5; i++){
			// pensa ...
			pensaMuito(filosofo);
			// pega garfo da esquerda
			pegaGarfo(/*posiçao*/filosofo, /*dono*/filosofo);
			// pega garfo da direita
			pegaGarfo(/*posiçao*/(filosofo+1)%N, /*dono*/filosofo);
			// fatura o espaguete
			comeEspaguete(filosofo);
			// larga o garfo da esquerda
			largaGarfo(/*posiçao*/filosofo, /*dono*/filosofo);
			// larga o garfo da direita
			largaGarfo(/*posiçao*/(filosofo+1)%N, /*dono*/filosofo);
		}
	}
	
	private void pensaMuito(int fil){
		switch (fil) {
			case 0: // filosofo 0 pensa por 1000 ms...
				try{ 
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(500);}
				catch (InterruptedException e){}
			case 1: // filosofo 1 pensa por 2000 ms...
				try{ 
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(1000);}
				catch (InterruptedException e){}
			case 2: // filosofo 1 pensa por 3000 ms...
				try{ 
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(1500);}
				catch (InterruptedException e){}
			case 3: // filosofo 1 pensa por 4000 ms...
				try{
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(2000);}
				catch (InterruptedException e){}
			case 4: // filosofo 1 pensa por 5000 ms...
				try{
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(2500);}
				catch (InterruptedException e){}
		}		
	}

	private void pegaGarfo(int pos, int dono){
		if (((Garfo)garfos.get(pos)).getEstadoGarfo()==false){ // desocupado
			System.out.println("++>"+Thread.currentThread().getName()+" PEGA GARFO "+ pos);
			((Garfo)garfos.get(pos)).setEstadoGarfo(true); // pega garfo
			((Garfo)garfos.get(pos)).setDonoGarfo(dono); // pega garfo
		}
	}
	
	private void largaGarfo(int pos, int dono){
		if (((Garfo)garfos.get(pos)).getEstadoGarfo()==true &&
			((Garfo)garfos.get(pos)).getDonoGarfo() == dono){ // desocupado
			System.out.println("-->"+Thread.currentThread().getName()+" LARGA GARFO "+ pos);
			((Garfo)garfos.get(pos)).setEstadoGarfo(false); // garfo liberado
			((Garfo)garfos.get(pos)).setDonoGarfo(-1); // garfo sem dono
		}
	}
	
	private void comeEspaguete(int fil){
		// se ambos os garfos estiverem reservados pelo
		// filosofo "fil", então ele come espaguete...
		// Testar a sua solução de proteção, comente o if, deixando apenas o 
		// seu conteúdo liberado
		if (((Garfo)garfos.get(fil)).getEstadoGarfo() &&
			((Garfo)garfos.get((fil+1)%N)).getEstadoGarfo() &&
			((Garfo)garfos.get(fil)).getDonoGarfo()==fil &&
			((Garfo)garfos.get((fil+1)%N)).getDonoGarfo()==fil){
			System.out.println("@@>"+Thread.currentThread().getName()+" COME ESPAGUETE");
			try{ Thread.sleep(5000);}
			catch (InterruptedException e){}
			}
	}
}
package jantarFilosofico;

import java.util.ArrayList;
import java.util.List;

import jantarFilosofico.Garfo;

public class JantarMain {

	public static void main(String[] args) {
	
		// cria os grafos (coleção de 5 garfos)
				List<Garfo>garfos = new ArrayList<Garfo>();
				for (int i = 0; i<=4; i++){
					Garfo garfo = new Garfo(i);
					garfos.add(i,garfo);
				}
				// cria a thread do filosofo 0
				Filosofo r0 = new Filosofo(garfos, 0);
				Thread f0 = new Thread(r0);
				// cria a thread do filosofo 1
				Filosofo r1 = new Filosofo(garfos, 1);
				Thread f1 = new Thread(r1);
				// cria a thread do filosofo 2
				Filosofo r2 = new Filosofo(garfos, 2);
				Thread f2 = new Thread(r2);
				// cria a thread do filosofo 3
				Filosofo r3 = new Filosofo(garfos, 3);
				Thread f3 = new Thread(r3);
				// cria a thread do filosofo 4
				Filosofo r4 = new Filosofo(garfos, 4);
				Thread f4 = new Thread(r4);		
				
				// nomeia as threads
				f0.setName("F0");
				f1.setName("F1");
				f2.setName("F2");
				f3.setName("F3");
				f4.setName("F4");
				
				// manda as threads pra fila de pronto
				f0.start();
				f1.start();
				f2.start();
				f3.start();
				f4.start();
			}

	

}

A segunda é com synchronized, que é onde não estou conseguindo fazer, se alguém puder me ajuda agradeço.

4 Respostas

nel

Você sabe qual a função do synchronized ?
E como exatamente o programa deve se comportar ? Que garantias você quer/precisa de modo que irá aplicar o synchronized ?

gomesrod

Olá,

Sobre o synchronized, ele não é "uma segunda solução", é um recurso a mais para evitar alguns problemas de concorrência.
Vou mostrar um desses problemas no seu programa. Os objetos Garfo são os concorridos, e é sobre eles que deve ser dada mais atenção nesse sentido.

Veja esse caso:

private void pegaGarfo(int pos, int dono){  
        if (((Garfo)garfos.get(pos)).getEstadoGarfo()==false){ // desocupado  
            System.out.println("++>"+Thread.currentThread().getName()+" PEGA GARFO "+ pos);  
            ((Garfo)garfos.get(pos)).setEstadoGarfo(true); // pega garfo  
            ((Garfo)garfos.get(pos)).setDonoGarfo(dono); // pega garfo  
        }  
    }

Imagine a thread "T1" está executando este trecho, entre as linhas 3 e 4 (ou seja, já entrou no IF mas ainda não setou o estadoGarfo). Nesse exato momento, o agendador de threads decide que vai colocá-la em espera, e acorda a thread "T2". Ela passa pelo IF, pois estadoGarfo ainda é false. Teremos então duas threads dentro desse trecho, o que não deveria acontecer.
Ou depois de executar tudo isso, a thread "T3" - que estava parada dentro do método largaGarfo - pode acordar e limpar o estado do garfo...
enfim, várias situações estranhas podem acontecer, e é para corrigir isso que serve o syncronized.

D

Estava dando uma lida sobre na função do synchronized, seguindo esses linkes:

http://www.guj.com.br/articles/43

http://www.guj.com.br/java/57761-synchronized-em-metodos-static

http://www.guj.com.br/java/115537-synchronized-em-thread

Preciso garantir que a estratégia usando de sincronismo está garantindo que qualquer garfo só será pego se não estiver com nenhum filosofo.

Estou pensando em aplicar o synchronized em cada um dos metados.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Editando para não gerar double post.

Tipo retirei os if de dentro dos metados já que uma Thread só poderar acessar o metado se tiver permição.
Gerol o seguindo problema, a Thread 1 pega os garfos 0 e 1 e logo em seguindo outra Thread pega os msm garfos, sem que a Thread o tenha largado.
Alguem sabe como resolvo este problema sem por os if de volta.

O código ficou assim:

private synchronized void pensaMuito(int fil){
				try{ 
					System.out.println("!!>"+Thread.currentThread().getName()+" PENSA");
					Thread.sleep(500);}
				catch (InterruptedException e){}
	}

	private synchronized void pegaGarfo(int pos, int dono){
			System.out.println("++>"+Thread.currentThread().getName()+" PEGA GARFO "+ pos);
			((Garfo)garfos.get(pos)).setEstadoGarfo(true); // pega garfo
			((Garfo)garfos.get(pos)).setDonoGarfo(dono); // pega garfo
		
	}
	
	private synchronized void largaGarfo(int pos, int dono){
			System.out.println("-->"+Thread.currentThread().getName()+" LARGA GARFO "+ pos);
			((Garfo)garfos.get(pos)).setEstadoGarfo(false); // garfo liberado
			((Garfo)garfos.get(pos)).setDonoGarfo(-1); // garfo sem dono
		
	}
	
	private synchronized void comeEspaguete(int fil){
			System.out.println("@@>"+Thread.currentThread().getName()+" COME ESPAGUETE");
			try{ Thread.sleep(5000);}
			catch (InterruptedException e){}
		
	}

EDIT:
RESOLVIDO, OBRIGADO.

J

E qual foi a solução? Seria interessante ter a solução no tópico para evitar a criação de um novo uma vez que estou no mesmo estado do seu ultimo post.

Criado 18 de abril de 2012
Ultima resposta 10 de set. de 2012
Respostas 4
Participantes 4