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!
Parabéns pela explicação, código bem comentado e pelo JFrame bem ilustrativo.
ResponderExcluirAdorei. Só n vou copiar e colar, vou tentar fazer passo-a-passo !!
ResponderExcluirta bom. vamos fingir que acredito. vc copiou td
ResponderExcluirEu sou formado em sistemas de informação :)
ExcluirAcho 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 .*-*
ExcluirAmigo, qual a funcionalidade desse trecho "// ATIVA A SINCRONIA
ResponderExcluirToolkit.getDefaultToolkit().sync();"?
Comentei ele e não vi a diferença, poderia me explicar?
Muito bom
ResponderExcluir