Baixe o app para aproveitar ainda mais
Prévia do material em texto
- -1 PROGRAMAÇÃO ORIENTADA A OBJETOS DESENVOLVENDO APLICATIVOS COM THREADS, CONCORRÊNCIA E GUI SWING - -2 Olá! Nesta aula, você irá: 1. Aprender a utilizar múltiplos Threads separados para modificar conteúdos exibidos em uma GUI Swing. 2. Aprender a trabalhar de forma sincronizada com cada um dos Threads. 3. Aprender a criar e executar um aplicativo de múltiplos threads. Threads e Swing Os componentes GUI Swing não são seguros quanto ao uso de Threads, quer dizer que se múltiplas Threads acessam um componente Swing, os resultados podem não estar corretos. Todas as interações com componentes Swing devem ser executadas a partir de uma Thread de cada vez. Normalmente, esta Thread é uma Thread de tratamento ou execução de eventos. É fundamental que uma tela GUI Swing não congele, podendo responder ao usuário sempre que necessário. Então, para criar um programa que responda as necessidades do usuário, o programador precisa aprender como a estrutura Swing trabalha com Threads: • Thread Inicial: executa o código inicial da aplicação • Thread de tratamento de evento: onde todo o tratamento de eventos é feito. A maioria das interações com componentes Swing devem ser feitos através deste Thread. • Thread de trabalho – worker: processos executados em segundo plano, que consomem processamento. Tratamento de Evento – Event dispatching A classe SwingUtilities fornece dois métodos para ajudar a fazer o tratamento de eventos na execução do programa. São eles: • invokeLater(): Solicita que algum código seja executado na Thread de despacho de evento. Este método retorna imediatamente a execução do código, sem que seja necessário esperar. • invokeAndWait(): Atua muito semelhante ao invokeLater(), exceto pelo fato de esperar pela execução do código. Como regra, procure utilizar sempre o método invokeLater() ao invés deste. • • • • • - -3 Produtor / Consumidor com Buffer e GUI Vamos implementar um algoritmo Produtor / Consumidor com um Buffer de cinco posições: Este programa será composto por: Produtor inteiro Classe responsável pela produção do conteúdo e disponibilizado na área compartilhada; - -4 Consumidor inteiro Classe responsável pelo consumo do conteúdo disponibilizado na área compartilhada; B u f f e r Inteiro Sincronizado O local onde será feito a sincronização da leitura e da escrita em um buffer de cinco posições; Atualiza Thread Thread responsável pela comunicação entre os Threads e a Interface Gráfica; B u f f e r Rotativo Classe responsável pela configuração, disparo e recebimento das informações dos produtores e consumidores. É onde se encontra o procedimento main(); Podemos observar que foi colocado uma configuração para iniciar a produção. A produção é iniciada ao clicar no botão Inicia Produção. Este fica desabilitado até que a o Consumidor consuma todos os 10 elementos. As barras de rolagem representam o tempo que queremos esperar para produzir ou consumir. É o tempo de simulação ocupada, na qual o Thread dorme. Vamos ver a saída do programa: - -5 O Produtor Inteiro – ProdutorInteiro O código do produtor inteiro está apresentado abaixo: import javax.swing.SwingUtilities; /** * * @author Eduardo */ public class ProdutorInteiro extends Thread { private BufferInteiroSincronizado objetoCompartilhado; private BufferRotativo areaSaida; private int tempoProducao = 500; public ProdutorInteiro(BufferInteiroSincronizado compartilhado, BufferRotativo saida){ super("ProdutorInteiro"); objetoCompartilhado = compartilhado; areaSaida = saida; } public ProdutorInteiro(BufferInteiroSincronizado compartilhado, BufferRotativo saida, float tempo){ super("ProdutorInteiro"); objetoCompartilhado = compartilhado; areaSaida = saida; tempoProducao = (int)(1000*tempo); } public void run(){ for (int cont=1; cont<=10; cont++){ try{ Thread.sleep((int)(Math.random()*tempoProducao)); } catch(InterruptedException exception){ System.err.println(exception.toString()); } - -6 objetoCompartilhado.setSharedInt(cont); } SwingUtilities.invokeLater(new AtualizaThread(areaSaida, "\n"+ getName() + " Finalizando de produzir valores"+ "\nTerminando "+getName()+"\n", false)); } } Podemos observar que para o Thread falar com a GUI, ele precisa do SwingUtilities chamando o Thread AtualizaThread. Tanto o produtor quanto o consumidor vão receber via parâmetro a GUI (BufferRotativo) e o tempo de espera. Consumidor Inteiro – ConsumidorInteiro Agora vamos observar o código do Consumidor: import javax.swing.SwingUtilities; /** * * @author Eduardo */ public class ConsumidorInteiro extends Thread{ private BufferInteiroSincronizado objetoCompartilhado; private BufferRotativo areaSaida; private int tempoConsumidor=3000; public ConsumidorInteiro (BufferInteiroSincronizado compartilhado, BufferRotativo saida){ super("ConsumidorInteiro"); objetoCompartilhado = compartilhado; areaSaida= saida; } public ConsumidorInteiro (BufferInteiroSincronizado compartilhado, BufferRotativo saida, float tempo){ super("ConsumidorInteiro"); objetoCompartilhado = compartilhado; areaSaida= saida; - -7 tempoConsumidor = (int)(1000*tempo); } @Override public void run(){ int valor, soma=0; do{ try{ Thread.sleep((int)(Math.random()*tempoConsumidor)); } catch(InterruptedException exception){ System.err.println(exception.toString()); } valor=objetoCompartilhado.getSharedInt(); soma+=valor; }while(valor!=10); SwingUtilities.invokeLater(new AtualizaThread(areaSaida, "\n"+getName()+" consumido valores totalizando: "+soma+ "\nTerminando "+getName()+"\n", true)); } } Não há nada de diferente que seja necessário um comentário. Área Compartilhada – BufferInteiroCompartilhado Vamos ver o código: import java.text.DecimalFormat; import javax.swing.*; /** * * @author Eduardo */ public class BufferInteiroSincronizado { private int sharedInt [] = {-1, -1, -1, -1, -1}; private boolean escrever = true; - -8 private boolean ler = false; private int posicaoLeitura = 0, posicaoEscrita = 0; private BufferRotativo saidaArea; public BufferInteiroSincronizado (BufferRotativo saida){ saidaArea = saida; } public synchronized void setSharedInt(int valor){ while(!escrever){ try{ SwingUtilities.invokeLater( new AtualizaThread( saidaArea, " Esperando para Produzir " + valor, false)); wait(); } catch(InterruptedException exception){ System.err.println(exception.toString()); } } sharedInt[posicaoEscrita]=valor; ler=true; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\nProduzido " + valor+" na celula "+posicaoEscrita, false)); posicaoEscrita = (posicaoEscrita + 1)%5; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\tEscrita " + posicaoEscrita + "\tLeitura "+ posicaoLeitura, false)); displayBuffer(saidaArea, sharedInt); if(posicaoEscrita==posicaoLeitura){ escrever= false; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\nBUFFER LOTADO", false)); } notify(); } - -9 public synchronized int getSharedInt(){ int valor; while(!ler){ try{ SwingUtilities.invokeLater(new AtualizaThread(saidaArea, " ESPERANDO PARA CONSUMIR", false)); wait(); } catch(InterruptedException exception){ System.err.println(exception.toString()); } } escrever = true; valor = sharedInt[posicaoLeitura]; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\nConsumido "+valor+" da célula "+posicaoLeitura, false)); posicaoLeitura=(posicaoLeitura + 1) % 5; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\tEscrita "+ posicaoEscrita +"\tLeitura"+ posicaoLeitura, false)); displayBuffer(saidaArea, sharedInt); if(posicaoLeitura==posicaoEscrita){ ler=false; SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\nBUFFER VAZIO", false)); } notify(); return valor; } public void displayBuffer(BufferRotativo AreaSaida, int buffer[]){ DecimalFormat formatoNumero = new DecimalFormat(" #;-#" ); StringBuffer bufferSaida = new StringBuffer(); - -10 for (int cont=0; cont<buffer.length;cont++)bufferSaida.append(" "+formatoNumero.format(buffer[cont])); SwingUtilities.invokeLater(new AtualizaThread(saidaArea, "\tbuffer: "+bufferSaida, false)); } } Esta classe possui um buffer de inteiros de cinco posições. Observe como é feita a atribuição dos novos valores de leitura e escrita: posicaoLeitura=(posicaoLeitura + 1) % 5; Nesta linha, posiçãoLeitura recebe um valor de 1 até 5 independente do valor da posição. Isso se dá porque estamos pegando o resto da divisão com o operador %. Neste algoritmo teremos um controle de posicionamento para leitura e outro para escrita. É fundamental também identificar se o Buffer está cheio ou vazio. Em ambos os casos, algum Thread irá dormir. No caso do Buffer cheio, o produtor precisa esperar o consumidor consumir enquanto que se o Buffer estiver vazio, o consumidor não pode consumir e precisa esperar. Para mostrar como está sendo a evolução do programa e dos Threads, a comunição com a GUI é feita através do SwingUtilities. Os métodos Get’s e Set’s determinam como o produtor e como o consumidor irão compartilhar os recursos. Eles têm algoritmos próprios para controlar o funcionamento dos Threads. Existe também o método DisplayBuffer que serve somente para formatar o buffer para que ele possa ser apresentado decentemente. Atualiza GUI – AtualizaThread Este Thread é o responsável pela comunicação entre os Threads e a GUI. Tudo que o usuário precisar manipular na tela, precisará ter um método e deverá ser acessado neste objeto. No método run(), o Thread chama dois métodos do Objeto principal, que são adiciona e habilitaProdução. O método adiciona acrescenta uma linha no Componente JTextArea. Já o método habilitaProdução recebe um booleano para verificar se o conjunto de Threads ainda está produzindo ou não. Quando é encerrado a produção, é enviado um sinal true para este método para que o botão de início de produção seja habilitado. Com isso, podemos controlar a nossa tela dinamicamente, sempre que ocorre alguma coisa nos Threads. No caso, foi implementado apenas uma área de texto para receber mensagens e a habilitação de um botão. Vamos ver o código: public class AtualizaThread extends Thread{ - -11 private BufferRotativo saidaArea; private String msgSaida; private boolean habilita; public AtualizaThread(BufferRotativo saida, String msg, boolean h){ saidaArea = saida; msgSaida = msg; habilita = h; } public void run(){ saidaArea.adiciona(msgSaida); saidaArea.habilitaProducao(habilita); } } Para finalizarmos o assunto desta aula, leia "Programa Principal - BufferRotativo", no link: http://estaciodocente.webaula.com.br/cursos/gon282/docs/14POO_aula10_doc01.pdf CONCLUSÃO Nesta aula, você: • Aprendeu a utilizar múltiplos Threads separados para modificar conteúdos exibidos em uma GUI Swing. • Aprendeu a trabalhar de forma sincronizada com cada um dos Threads. • Aprendeu a criar e executar um aplicativo de múltiplos threads. Saiba mais Para esta aula sugiro as seguintes tarefas: • Leitura do Capítulo 23 (23.6 até 23.7) do livro Java como programar, 6ª Edição, de H. Deitel e P. Deitel, Editora Pearson Education, 2005. • Resolução dos exercícios de 23.5 até 23.9 do capítulo 23 do livro Java Como Programar – 6ª Edição. • • • • • http://estaciodocente.webaula.com.br/cursos/gon282/docs/14POO_aula10_doc01.pdf Olá! CONCLUSÃO
Compartilhar