Jantar dos filósofos com synchronized

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]

[code]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;
	}

}

[/code]

[code]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 garfos; // garfos disponíveis 0, 1, 2, 3 e 4
int filosofo;
Filosofo (List 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){}
		}
}

}
[/code]

[code]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();
		}

}
[/code]

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

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 ?

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:

[code]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  
    }  
}  [/code]

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.

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

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

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:

[code]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){}
	
}[/code]

EDIT:
RESOLVIDO, OBRIGADO.

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.