quarta-feira, 3 de julho de 2013

[Tutorial] [Java] Aplicação em Thread - Jantar dos Filósofos

E ai pessoal?! To sumido né?! ( TRÁGICO hahahahaha mas é que estou desenvolvendo muitos tutoriais para hora de postar postar eles em um ordem sem intervalos mas enfim... ) Neste tutorial vamos aprender a trabalhar com Threads ( UHUUUUU não sabe o que é isso!? Eu explico ). Thread é um recurso de multi-processamento disponível para aplicações, ou seja, existem vários processos em execução, porém cada um tem a sua hora de atuar sobre o outro.

Um exemplo clássico para entender para que servem e como funcionam os Threads é o Jantar dos Filósofos! Bom inicialmente observe a imagem abaixo.


Acima, podemos notar a presença de cinco pratos com espaguete ( HMMMMMMMM ) cada um desses pratos é para um filósofo, ou seja, existem cinco filósofos no ambiente. Porém, contudo, entretanto, também só existe cinco talheres e para um filósofo comer sua comida ele precisa de dois talheres ( na figura são garfos mas no conceito inicial são palitinhos chineses, os hashis ). Ou seja, temos cinco pratos com cinco filósofos, que terão que compartilhar cinco talheres, então um filósofo só pode pegar um talher que esteja disponível, assim como deve liberar o talher quando não utilizável ou quando estiver somente com um. Em termos da programação, isso significa que teremos 5 objetos (filósofos) e 5 objetos compartilhados (os talheres). Nesse termo que entra o conceito de Thread pois para o compartilhamento múltiplos de objetos é necessária uma programação simultânea, ou seja, todos filósofos "vivos" e todos talheres manipuláveis.

Agora vou apresentar a vocês um diagrama de classes, UHUUUUUU, este diagrama funciona como uma espécie de mapa para desenvolver nossas classes, métodos e atributos, para satisfazer a problematização descrita acima.


Bom acima podemos ver que desenvolveremos as classes: Filosofo ( Representa os filósofos em si, e o que eles são capazes de fazer na mesa ), Semaforo ( Define uma propriedade para mostrar aos outro filósofos 0 se estiver livre para uso, 1 se estiver ocupado para uso ), Grade ( Que cria o ambiente e exibe na tela ) e JantarDosFilosofos ( Nosso método principal que implementa nossa Grade ). O nome do pacote é JANTARDOSFILOSOFOS. Vamos as classes, e apreciem os comentários.

CLASSE FILOSOFO

package JANTARDOSFILOSOFOS;

/*
 *
 * AUTOR: CAIQUE MONTEIRO ARAUJO
 * DATA: 03/07/2013
 * CLASSE: FILOSOFO
 * OBJETIVO: CRIA UM OBJETO REPRESENTATIVO PARA O FILÓSOFO QUE PODERÁ COMER,
 *           PENSAR E ESTAR COM FOME.
 *
 */

// Cria uma extensão para a classe Thread
public class Filosofo extends Thread
{

    // Cria um código privado para o filósofo
    private int ID;

    // Cria padrões de comportamento do filósofo
    final int PENSANDO = 0;
    final int FAMINTO  = 1;
    final int COMENDO  = 2;

    // Método construtor que recebe um nome para a classe e um código de
    // identificação do filósofo
    public Filosofo (String nome, int ID)
    {
        super(nome);
        this.ID = ID;
    }

    // Método para definir que o filósofo está com fome
    public void ComFome ()
    {
        // Seta o estado deste filósofo na classe Grade para FAMINTO
        Grade.estado[this.ID] = 1;
        // Exibe uma mensagem de controle na tela
        System.out.println("O Filósofo " + getName() + " está FAMINTO!");
    }

    // Método para definir que o filósofo está comendo
    public void Come ()
    {
        // Seta o estado deste filósofo na classe Grade para COMENDO
        Grade.estado[this.ID] = 2;
        // Exibe uma mensagem de controle na tela
        System.out.println("O Filósofo " + getName() + " está COMENDO!");

        // Será criado um controle para o filósofo permanecer comendo
        // durante certo período de tempo
        try
        {
            // Fica parado neste estado por 1000 milisegundos
            Thread.sleep(1000L);
        }
        catch (InterruptedException ex)
        {
            // Exibe uma mensagem de controle de erro
            System.out.println("ERROR>" + ex.getMessage());
        }
    }

    // Método para definir que o filósofo está pensando
    public void Pensa ()
    {
        // Seta o estado deste filósofo na classe Grade para PENSANDO
        Grade.estado[this.ID] = 0;
        // Exibe uma mensagem de controle na tela
        System.out.println("O Filósofo " + getName() + " está PENSANDO!");

        // Será criado um controle para o filósofo permanecer pensando
        // durante certo período de tempo
        try
        {
            // Fica parado neste estado por 1000 milisegundos
            Thread.sleep(1000L);
        }
        catch (InterruptedException ex)
        {
            // Exibe uma mensagem de controle de erro
            System.out.println("ERROR>" + ex.getMessage());
        }
    }

    // Método para o filósofo soltar um garfo que ele pegou
    public void LargarGarfo ()
    {
        // Decrementa o semáforo mutex principal da classe, isso permite
        // informar que o atual método está operando na mesa dos filósofos
        Grade.mutex.decrementar();

        // Coloca o filósofo para pensar determinado tempo
        Pensa();

        // Após o filósofo pensar, ele vai informar para os seus vizinhos
        // que podem tentar pegar os garfos que já estão disponíveis
        Grade.filosofo[VizinhoEsquerda()].TentarObterGarfos();
        Grade.filosofo[VizinhoDireita()].TentarObterGarfos();

        // Após operar, volta o semáforo mutex para o estado normal
        // indicando que já realizou todos procedimentos na mesa
        Grade.mutex.incrementar();
    }

    // Método para o filósofo pegar um garfo na mesa
    public void PegarGarfo ()
    {
        // Decrementa o semáforo mutex principal da classe, isso permite
        // informar que o atual método está operando na mesa dos filósofos
        Grade.mutex.decrementar();

        // Deixa o filósofo faminto por determinado tempo
        ComFome();

        // Após o filósofo o período de fome, ele vai verificar com seus
        // vizinhos se ele pode pegar os garfos
        TentarObterGarfos();

        // Após operar, volta o semáforo mutex para o estado normal
        // indicando que já realizou todos procedimentos na mesa
        Grade.mutex.incrementar();
        // Decrementa seu semáforo
        Grade.semaforos[this.ID].decrementar();
    }

    // Método para verificar se o filósofo pode pegar um garfo disposto na mesa
    public void TentarObterGarfos()
    {

        // Verifica se este filósofo está com fome, e se o vizinho da esquerda
        // e da direita não estão comendo
        if (Grade.estado[this.ID] == 1 &&
            Grade.estado[VizinhoEsquerda()] != 2 && 
            Grade.estado[VizinhoDireita()] != 2)
        {
            // Então este filósofo pode comer
            Come();
            // E incrementa o seu semáforo
            Grade.semaforos[this.ID].incrementar();
        }

    }

    // Método de execução da classe, onde o ambiente do filósofo será rodado
    @Override
    public void run ()
    {

        try
        {
            // Coloca o filósofo para pensar
            Pensa();

            // Então realiza uma vida infinita para o filósofo onde inicialmente
            // ele executa os procedimentos de pergar os garfos da mesa, posteriormente
            // ele descansa um pouco, e por fim, ele largar os garfos que ele pegou
            do
            {
                PegarGarfo();
                Thread.sleep(1000L);
                LargarGarfo();
            }
            while (true);
        }
        catch (InterruptedException ex)
        {
            // Exibe uma mensagem de controle de erro
            System.out.println("ERROR>" + ex.getMessage());
            // E da um retorno de cancelamento
            return;
        }

    }

    // Método para obter o filósofo vizinho da direita
    public int VizinhoDireita ()
    {
        // Rationa o valor em 5 posições, ou seja, se o ID deste filósofo acrescentado
        // de um for maior que quatro, passa a ser zero
        return (this.ID + 1) % 5;
    }

    // Método para obter o filósofo vizinho da esquerda
    public int VizinhoEsquerda ()
    {
        if (this.ID == 0)
        {
            // Retorna a ultima posição
            return 4;
        }
        else
        {
            // Rationa o valor em 5 posições, ou seja, se o ID deste filósofo decrescido
            // de um for menor que zero, passa a ser quatro
            return (this.ID - 1) % 5;
        }
    }

}

CLASSE SEMAFORO

package JANTARDOSFILOSOFOS;

/*
 *
 * AUTOR: CAIQUE MONTEIRO ARAUJO
 * DATA: 03/07/2013
 * CLASSE: SEMAFORO
 * OBJETIVO: CONTROLA O CONTADOR DA APLICAÇÃO
 *
 */

public class Semaforo
{

    // Criação de um contador protegido para esta classe
    protected int contador;

    // Método construtor da classe que não recebe nenhum valor
    public Semaforo ()
    {
        this.contador = 0;
    }

    // Método construtor da classe que recebe um valor para setar no
    // contador
    public Semaforo (int valor)
    {
        this.contador = valor;
    }

    // Método de sincronização da classe onde será decrescido o contador
    public synchronized void decrementar ()
    {

        // Enquanto o contador for igual a 0, ele aguarda e trata a exceção
        while (this.contador == 0)
        {
            try
            {
                // Espera uma nova solicitação
                wait();
            }
            catch (InterruptedException ex)
            {
                // Exibe uma mensagem de controle de erro
                System.out.println("ERROR>" + ex.getMessage());
            }
        }

        // Caso tenha saído do while acima, então decrementa o
        // contador da classe
        this.contador--;

    }

    // Método de sincronização da classe onde será incrementado o contador
    public synchronized void incrementar ()
    {
        // Incrementa o contador da classe
        this.contador++;
        // Notifica que a solicitação já foi executada
        notify();
    }

}

CLASSE GRADE

package JANTARDOSFILOSOFOS;

import javax.swing.JPanel;
import java.awt.*;

/*
 *
 * AUTOR: CAIQUE MONTEIRO ARAUJO
 * DATA:
 * CLASSE:
 * OBJETIVO: 
 *
 */

public class Grade extends JPanel implements Runnable
{

    // Cria padrões de comportamento dos filósofos
    final int PENSANDO = 0;
    final int FAMINTO  = 1;
    final int COMENDO  = 2;

    // Mensagem para cada um dos estados
    String mensagem = "";

    // Thread principal da aplicação
    Thread animador;

    // Criação dos semáforos da aplicação

    // O semáforo mutex que recebe o valor incial 1 para o contador
    // e é o semáforo principal da nossa aplicação
    public static Semaforo mutex = new Semaforo(1);

    // O vetor semáforos são normais e existe um semáforo para cada filósofo
    // que será criado, esses semafóros não recebem valores de inicialização
    // portanto iniciando o contador em 0
    public static Semaforo semaforos[] = new Semaforo[5];

    // Define um vetor para o estado de cada um dos filósofos presentes
    // na aplicação
    public static int estado[] = new int[5];

    // Cria 5 filósofos em um vetor para a aplicação
    static Filosofo filosofo[] = new Filosofo[5];

    // Método construtor da Grade da aplicação
    public Grade ()
    {
        // Define o foco para este JPanel
        setFocusable(true);

        // Define um tamanho para a tela
        setSize(400, 400);
        
        // Seta a cor do fundo
        setBackground(Color.white);
        
        init();
    }

    // Método para inicializar tudo o que é preciso dentro da classe
    public void init ()
    {
        // Inicializa todos estados para zero
        for (int i = 0; i < estado.length; i++)
        {
            estado[i] = 0;
        }

        // Verifica se o Thread de animação é vazio
        if(animador == null)
        {
            // Então cria um novo Thread
            animador = new Thread(this);
            // Inicia sua execução
            animador.start();
        }

        // Define a prioridade principal para este atual Thread
        Thread.currentThread().setPriority(1);

        // Inicializa todos filósofos
        filosofo[0] = new Filosofo("Platao", 0);
        filosofo[1] = new Filosofo("Socrates", 1);
        filosofo[2] = new Filosofo("Aristoteles", 2);
        filosofo[3] = new Filosofo("Tales", 3);
        filosofo[4] = new Filosofo("Sofocles", 4);

        // Inicializa todos semáforos
        semaforos[0] = new Semaforo(0);
        semaforos[1] = new Semaforo(0);
        semaforos[2] = new Semaforo(0);
        semaforos[3] = new Semaforo(0);
        semaforos[4] = new Semaforo(0);

        // Inicia a execução de todos filósofos
        filosofo[0].start();
        filosofo[1].start();
        filosofo[2].start();
        filosofo[3].start();
        filosofo[4].start();
    }

    // Método para desenhar os objetos na tela da aplicação
    @Override
    public void paint(Graphics g)
    {
        super.paint(g);
        
        // Define a cor azul
        g.setColor(Color.blue);
        // Cria um circulo na posição (50,50) do plano cartesiano com tamanho
        // 300x300
        g.drawOval(50, 50, 300, 300);

        // Para cada um dos filósofos será feito um desenho
        for(int i = 0; i < 5; i++)
        {            
            // Define a cor para cara tipo de estado
            if(estado[i] == 0)
            {
                g.setColor(Color.gray);
                mensagem = "PENSANDO";
            }
            if(estado[i] == 1)
            {
                g.setColor(Color.yellow);
                mensagem = "FAMINTO";
            }
            if(estado[i] == 2)
            {
                g.setColor(Color.green);
                mensagem = "COMENDO";
            }
            
            // Desenha o filósofo, sua carinha e seu nome na tela
            // Define os planos (x,y) e posteriormente o tamanho do objeto a ser desenhado
            g.fillOval((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 15, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 15, 30, 30);
            g.setColor(Color.black);
            g.drawLine((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 5, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) + 5, (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) + 5, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) + 5);
            g.drawLine((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 2, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 3, (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) + 2, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)));
            g.drawLine((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 2, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)), (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) + 2, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)));
            g.drawLine((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 8, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 8, (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 3, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 8);
            g.drawLine((int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) + 3, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 8, (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) + 8, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) - 8);
            g.drawString(filosofo[i].getName(), (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 15, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) + 25);
            g.drawString(mensagem, (int)(200D - 100D * Math.cos(1.2566370614359172D * (double)i)) - 15, (int)(200D - 100D * Math.sin(1.2566370614359172D * (double)i)) + 40);
        }

        // ATIVA A SINCRONIA
        Toolkit.getDefaultToolkit().sync();
        // PAUSA
        g.dispose();

    }

    // Método de execução da classe, onde o ambiente será rodado
    public void run ()
    {        
        // Uma execução infinita
        do
        {
            // Redesenha a tela
            repaint();

            // Dorme durante um tempo para redesenhar novamente
            try
            {
                Thread.sleep(1000L);
            }
            catch (InterruptedException ex)
            {
                // Exibe uma mensagem de controle de erro
                System.out.println("ERROR>" + ex.getMessage());
            }
        }
        while (true);
    }

}


CLASSE JANTARDOSFILOSOFOS

package JANTARDOSFILOSOFOS;

import javax.swing.JFrame;

public class JantarDosFilosofos extends JFrame
{

    public JantarDosFilosofos ()
    {
        // CRIA UMA NOVA GRADE NA TELA
        add(new Grade());

        // DEFINE O TITULO
        setTitle("Jantar dos Filósofos");
        // INFORMA O MÉTODO DE SAÍDA
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        // SETA O TAMANHO
        setSize(410, 410);
        // SETA A LOCALIZAÇÃO
        setLocationRelativeTo(null);
        // SETA A VISIBILIDADE
        setVisible(true);
        // SETA SE É REDIMENSIONAVEL
        setResizable(false);
    }

    public static void main(String[] args)
    {
        new JantarDosFilosofos();
    }

}

Agora teste o programa, e tcharãnnnnnnnnnnn, muito bom né!? É isso aí pessoal, até a próxima!


7 comentários:

  1. Parabéns pela explicação, código bem comentado e pelo JFrame bem ilustrativo.

    ResponderExcluir
  2. Adorei. Só n vou copiar e colar, vou tentar fazer passo-a-passo !!

    ResponderExcluir
  3. ta bom. vamos fingir que acredito. vc copiou td

    ResponderExcluir
    Respostas
    1. Eu sou formado em sistemas de informação :)

      Excluir
    2. Acho que quando ele disse que "vc copiou td",ele estava se referindo a Emile Carine Santos Ramos e sobre o comentário de que não iria copiar o seu texto .*-*

      Excluir
  4. Amigo, qual a funcionalidade desse trecho "// ATIVA A SINCRONIA
    Toolkit.getDefaultToolkit().sync();"?
    Comentei ele e não vi a diferença, poderia me explicar?

    ResponderExcluir