TA345/531 TP257 Informática Aplicada à Engenharia de Alimentos.


Aula 6. Linhas de Execução (THREADS)

A incorporação de variáveis de condição permite utilizar recursos de computação concorrente de forma bem facilitada, o que possibilita uma melhoria de performance dos programas.

 

Neste capítulo, veremos o que são threads, analisaremos seus estados e métodos básicos e aprenderemos a inserir threads em Applets. Eles também são utilizados para efeitos de sincronização e detecção de erros.

 

6.1. O que são threads?

Thread se define como um fluxo de controle seqüencial dentro de um programa.

 

Como um programa seqüencial qualquer, uma thread tem um começo, um fim e uma seqüência de comandos. O thread não é independente, ele existe e se executa dentro de um programa.

 

Threads permitem que um programa simples possa executar várias tarefas diferentes ao mesmo tempo, independentemente umas das outras.

 

Programas multi-thread são programas que contêm várias threads executando tarefas distintas, simultaneamente. Pre exemplo , um browser (escrito em Java) pode fazer um scroll em uma página enquanto carrega uma imagem ou executa vários Applets ao mesmo tempo.

 

Em Java, threads são cidadãos de primeira ordem, se constituindo de instâncias da classe Thread que fornecem suporte a comunicação concorrente.

 

A classe Thread provê os métodos necessários para criar e controlar threads e executá-los concorrentemente.

 

O corpo de uma thread é o seu método run( ), e é nele que são executadas as tarefas as quais thread se destina.

 

Podemos implementar threads de duas maneiras:

1.  Criando uma subclasse da classe Thread e definindo o seu método run( ) de maneira adequada à realização da tarefa do thread;

2.  Criando uma instância de Thread que recebe como parâmetro um objeto que implemente a interface Runnable - este objeto providenciará o método run( ) para a thread.

 

A linguagem Java fornece meios para criarmos threads como daemons, agruparmos threads, sincronizá-los e controlar suas prioridades.

 

6.2. Os estados de uma thread

 

 

·                    New Thread

            Inicialização da thread - feita através do construtor Thread( ).

 

class MyThreadClass extends Thread

{

}

MyThreadClass myThread = new MyThreadClass( ) ;

 

Neste estado, nenhum recurso do sistema foi alocado para o thread ainda, assim, a partir daqui, tudo que você pode fazer é um start( ), para ativar a thread, ou um stop( ), para “matá-lo”. A chamada de qualquer outro método não faz sentido e levantará a exceção IllegalThreadStateException.

 

·                    Runnable

Este é o estado em que o thread está pronto para rodar. O método start( ) requisita os recursos do sistema necessários para rodar a thread e chama o seu método run( ). O método run( ) é a “alma” de um thread; é neste método que definimos o que a thread vai executar.

 

Thread myThread = new MyThreadClass( );

myThread.start( );

 

Falamos em Runnable, ao invés de Running, porque a thread pode não estar realmente sendo executada. Imagine um computador com um único processador - seria impossível executar todas as threads ao mesmo tempo. O que ocorre é que a CPU deve ser compartida sequencialmente entre as várias threads. Quando uma thread está Running, ela está também Runnable, as instruções do seu método run( ) é que estão sendo executadas pela CPU.

 

·                    Not Runnable

O estado Not Runnable significa que a thread está impedida de executar por alguma razão.

 

Existem 4 maneiras de uma thread ir para o estado Not Runnable.

receber a mensagem suspend( );

receber a mensagem sleep( );

thread bloqueia, esperando I/O;

a thread usa seu método wait( ) para esperar por uma variável de condição.

 

O exemplo abaixo coloca o applet myThread para dormir por 10 segundos:

Thread myThread = new MyThreadClass( );

myThread.start( );

try

{

myThread.sleep(10000);

}

catch (InterruptedException e) { }

 

Cada uma destas maneiras tem a sua forma específica de sair do estado Not Runnable.

1.  Se a thread foi suspensa, alguém precisa mandar-lhe a mensagem resume( );

2.  Se a thread foi posta para dormir, ela voltará a ser Runnable quando o número de milisegundos determinado passar;

3.  Se a thread está bloqueada, esperando por I/O, a operação precisa ser completada;

4.  Se a thread está esperando por uma variável de condição, o objeto que a retém precisa liberá-la, através de um notify( ) ou de um notifyAll( ).

 

·                    Dead

Uma thread pode morrer de “causas naturais” (quando o seu método run( ) acaba normalmente) ou pode ser morto pelo método stop( ). É possível controlar a ordem de execução de várias threads definindo prioridades para elas. O escalonador de threads do Java segue a seguinte regra: a qualquer instante, a thread corrente é a de maior prioridade. Para que a thread de maior prioridade ceda CPU a outra thread, ele precisa enviar para si o método yield( ), ou, entrar no estado Not Runnable. Caso contrário, ele irá executar até que termine seu método run( ). Para descobrir a prioridade de uma thread, podemos usar o método getPriority( ) e, para defini-la setPriority(n), onde n é um inteiro de 1 a 10 (10 representando a prioridade máxima).

 

6.3. Threads em Applets

Até agora, nós vimos como trabalhar com threads criadas a partir da classe Thread ou de uma classe que herde da classe Thread. Sabemos que esta classe provê os métodos básicos para se lidar com threads (run( ), start( ), stop( ), sleep( ), etc.).

 

Suponha que você queira, agora, implementar uma thread dentro de um Applet. Por exemplo, suponha que você quer fazer um Applet relógio, que atualiza o seu display a cada segundo. A classe que vai implementar o seu relógio precisa ser uma subclasse da classe Appiet para herdar todas as facilidades oferecidas por ela. Como fazê-la, então, herdar também da classe Thread? A interface Runnable é a solução!

 

Qualquer objeto que implemente a interface Runnable pode utilizar o seu método run( ) para ser executado como uma thread.

 

class Clock extends Applet implements Runnable

{

}

 

O Applet Clock precisa, agora, criar a sua própria thread. Isto é feito no seu método start( )

 

public void start( )

{

if ( clockThread = = null)

{

clockThread = new Thread(this, “Clock”);

clockThread.start( );

}

}

 

Veja a chamada ao construtor Thread (this, “Clock”). Ele recebe como primeiro argumento um objeto que implementa a Interface Runnable, e fornece o método run() da thread clockThread.

 

public void run( )

{

while (clockThread != null)

{

repaint( );

try {

clockThread.sleep(1000);

}

catch (InterruptedException e) { }

}

}

 

No método stop( ) do Applet Clock, temos que chamar também o método stop( ) da thread clockThread, caso contrário, a thread vai continuar executando e consumindo recursos mesmo depois que sairmos da página do Applet.

 

public void stop( )

{

clockThread.stop( );

clockThread = null;

}

 

Se você revisitar a página, o start( ) do Applet Clock é chamado novamente e uma nova thread é inicializada.

 

6.4. Como herdar de Thread x como implementar Runnable

Existem duas maneiras de implementar threads:

1.      Herdando da classe Thread ou de subclasses da classe Thread;

2.      Implementando a interface Runnable e criando uma thread (passando o objeto que implementa Runnable como argumento).

 

Qual opção utilizar? Se você precisa estender outra classe (o exemplo mais comum é a classe Applet), use Runnable. Abaixo o código completo do Applet Clock:

 

Exemplo 1 - Clock:

import java.awt.Graphics;

import java.util.Date;

 

/*<APPLET CODE=Clock.class WIDTH=200 HEIGHT=200></APPLET>*/

 

public class Clock extends java.applet.Applet implements Runnable

{

  Thread clockThread = null;

 

  public void start( )

  {

    if (clockThread == null)

    { clockThread = new Thread(this, "Clock");

      clockThread.start( );

    }

  }

 

  public void run( )

  {

    // loop terminates when clockThread is set to null in stop( )

    while (Thread.currentThread( ) == clockThread)

    { repaint( );

      try { clockThread.sleep(1000);}

      catch (InterruptedException e) {  }

    }

  }

  public void paint(Graphics g)

  { Date now = new Date( );

    g.drawString(

                        now.getHours( ) + ":" +

                        now.getMinutes( ) + ":" +

                        now.getSeconds( ), 5, 10);

  }

 

  public void stop( )  { clockThread = null; }

}

 

No exemplo seguinte, o recurso try - cartch é utilizado para evitar que o applet trave quando não houver números nas caixas de entrada de dados.

Exemplo 2 - Bloco completo:

import java.awt.*;

import java.applet.Applet;

import java.awt.event.*;

import java.lang.*;

 

public class blococompleto extends Applet implements ActionListener

{

    public TextArea ta;

    public Label legenda1,legenda2,legenda3,aviso;

    public TextField caixa1,caixa2,caixa3;

    public Button botao,btlimpacaixa,btlimpaarea,btatribui1,btatribui2;

    int x,y,z;

    messagebox m1;

    public void init()

    {

      legenda1= new Label("Digite o valor de X :");     add(legenda1);

      caixa1= new TextField("2",5);                     add(caixa1);

      legenda2= new Label("Digite o valor de y :");     add(legenda2);

      caixa2= new TextField("4",5);                     add(caixa2);

      legenda3= new Label("Digite o valor de z :");     add(legenda3);

      caixa3= new TextField("6",5);                     add(caixa3);

      botao= new Button ("Clique para processar");      add(botao); botao.addActionListener(this);

      ta= new TextArea("",16,30);                       add(ta);

      btlimpacaixa= new Button("Limpar as caixinhas");  add(btlimpacaixa);

      btlimpacaixa.addActionListener(this);

      btlimpaarea= new Button("Limpar a área de texto");add(btlimpaarea);

      btlimpaarea.addActionListener(this);

      btatribui1= new Button ("Valores com x positivo");add(btatribui1);

      btatribui1.addActionListener(this);

      btatribui2= new Button ("Valores com x negativo");add(btatribui2);

      btatribui2.addActionListener(this);

      aviso = new Label ("Não utilize botões de comando quando não existirem valores nas caixinhas!");

      aviso.setBackground(Color.red);

      add(aviso);

      ta.setForeground(Color.blue);

   }

 

   public void actionPerformed(ActionEvent e)

   {

   try

   {

      x=Integer.valueOf(caixa1.getText()).intValue();

      y=Integer.valueOf(caixa2.getText()).intValue();

      z=Integer.valueOf(caixa3.getText()).intValue();

 

      if (e.getSource()==botao)

      {

         ta.append("\n Fora do bloco");

         ta.append("\n x="+x);

         ta.append("\n y="+y);

         ta.append("\n z="+z);

 

         if(x<0)

         {  // início do bloco //

           int x=1;

           int y=2;

           int z=3;

           ta.append(" \n Dentro do bloco");

           ta.append("\n x="+x);

           ta.append("\n y="+y);

           ta.append("\n z="+z);

           //Final do bloco//

         }

 

         ta.append(" \n Dentro do bloco");

         ta.append("\n x="+x);

         ta.append("\n y="+y);

         ta.append("\n z="+z);

      }

 

      if(e.getSource()==btlimpacaixa)

      {

      caixa1.setText("0");

      caixa2.setText("0");

      caixa3.setText("0");

      }

 

      if(e.getSource()==btatribui1)

      {

      caixa1.setText("9");

      caixa2.setText("8");

      caixa3.setText("7");

      }

 

      if(e.getSource()==btatribui2)

      {

      caixa1.setText("-2");

      caixa2.setText("5");

      caixa3.setText("14");

      }

 

      if(e.getSource()==btlimpaarea)

      {

      ta.setText("");

      }

    }

    catch (Exception exp)

    {

       m1=new messagebox ("Erro:"+exp.toString());

       m1.setVisible(true);

 

    }

 

   } // fim action performed

 } // fim da public class

 

Complemento do Exemplo 3 - Bloco completo:

import java.awt.*;

import java.awt.event.*;

 

public class messagebox extends Frame implements ActionListener

{

  Panel p1,p2;

  Label l1;

  Button b1;

 

  public messagebox(String mens)

  {

   setLayout(new BorderLayout());

   setTitle("Mensagen");

   setBounds(100,100,400,150);

 

   p1= new Panel();   add("North",p1);

   l1= new Label (mens);   p1.add(l1);

 

   p2= new Panel();   add ("South",p2);

   b1= new Button ("OK");  p2.add(b1);

   b1.addActionListener(this);

  }

 

  public void actionPerformed(ActionEvent e)

  {

    if (e.getSource()==b1)

    {

      setVisible(false);

    }

  }

}

 

 

 


Volta para a pagina principal

 

Última atualização:  08/abril/2009