Utilizando threads

Utilizando Threads - parte 1

Para entender o funcionamento de uma thread é necessário analisar, inicialmente, um processo. A maioria dos sistemas de hoje são baseados em computadores com apenas um processador que executam várias tarefas simultâneas. Ou seja, vários processos que compartilham do uso da CPU tomando certas fatias de tempo para execução. A esta capacidade é denominado o termo multiprocessamento. Teoricamente existe uma grande proteção para que um processo não afete a execução de outro, modificando, por exemplo, a área de dados do outro processo, a menos que haja um mecanismo de comunicação entre os processos (IPC – Inter Process Communication). Este alto grau de isolamento reduz os desagradáveis GPFs (General Protection Fault), pois o sistema se torna mais robusto. Em contrapartida, o início de cada processo é bastante custoso, em termos de uso de memória e desempenho, e o mecanismo de troca de mensagens entre os processos é mais complexo e mais lento, se comparado a um único programa acessando a própria base de dados. Uma solução encontrada foi o uso de threads, (também conhecidas por linhas de execução). A thread pode ser vista como um subprocesso de um processo, que permite compartilhar a sua área de dados com o programa ou outras threads. O início de execução de uma thread é muito mais rápido do que um processo, e o acesso a sua área de dados funciona como um único programa. Existem basicamente duas abordagens para a implementação das threads na JVM: utilização de mecanismos nativos de operação do S.O., e a implementação completa da operação thread na JVM. A diferença básica é que as threads com mecanismos nativos do S.O. são mais rápidas. Em contrapartida a implementada pela JVM tem independência completa de plataforma. Basicamente, em ambos os casos, a operação das mesmas é obtida através de uma fatia de tempo fornecida pelo S.O. ou pela JVM. Isto cria um paralelismo virtual, como pode ser observado na figura abaixo, que representa a execução de três threads.

 

Estado de uma thread



A execução de uma thread pode passar por quatro estados: novo, executável, bloqueado e encerrado.



A thread está no estado de novo, quando é criada. Ou seja, quando é alocada área de memória para ela através do operador new.Ao ser criada, a thread passa a ser registrada dentro da JVM, para que a mesma posso ser executada. 
A thread está no estado de executável, quando for ativada. O processo de ativação é originado pelo método start(). É importante frisar que uma thread executável não está necessariamente sendo executada, pois quem determina o tempo de sua execução é a JVM ou o S.O.. 
A thread está no estado de bloqueado, quando for desativada. Para desativar uma thread é necessário que ocorra uma das quatro operações a seguir: 
1.Foi chamado o método sleep(long tempo) da thread; 
2.Foi chamado o método suspend() da thread (método deprecado) 
3.A trhead chamou o método wait(); 
4.A thread chamou uma operação de I/O que bloqueia a CPU; 


Para a thread sair do estado de bloqueado e voltar para o estado de executável, uma das seguintes operações deve ocorrer, em oposição as ações acima: 

Retornar após o tempo especificado, caso a thread estiver adormecida; 
Retornar através do método resume(), caso a thread tiver sido suspensa (método deprecado); 
Retornar com o método notify() (ou notifyAll()), caso a thread estiver em espera; 
Retornar após a conclusão da operação de I/O. 
A thread está no estado de encerrado, quando encerrar a sua execução. Isto pode acorrer pelo término do método run(), ou pela chamada explícita do método stop().

Começando a trabalhar com threads



Para entender o uso de uma thread, está apresentado a seguir, um programa que fica indefinidamente imprimindo um contador na saída padrão(SemThread.java).

 
 

public class SemThread {

        public static void main(String[] args) {

               int i = 0;

               while(true)

                       System.out.println("Número: "+ i++);

        }

}

 
 



Aparentemente este programa ocupa completamente a CPU, e é o que realmente ocorre em S.O.s corporativos (ex.:Windows 3x). Porém em S.O. preemptivos (ex.: Windows NT, Solaris, Windows 98, OS/2, etc), o próprio S.O. se encarrega de gerenciar a ocupação da CPU, o que permite rodar outros processos, mesmo que um processo não retorne o controle para o S.O.. Como implementar o programa SemThread, permitindo que outros processos compartilhem a CPU? A solução é utilizar threads, como pode ser observado na classe SimplesThread. Existem duas abordagens para uma classe ser uma thread: 
5.Implementar a interface Runnable; 
6.Ser derivada da classe Thread; 
Neste exemplo a classe Escrita é derivada da classe Thread. No método run() da classe Escrita está contido o código necessário para implementar adequadamente o programa acima.

 
 

class Escrita extends Thread {

        private int i;

        public void run() {

               while(true)

                       System.out.println("Número :"+ i++);

        }

}

 
 

 
 

public class SimplesThread1 {

        public static void main(String[] args) {

               Escrita e = new Escrita();   //Cria o contexto de execução

               e.start();                             //Ativa a thread

        }

}

 
 

Utilizando Threads
por Paulo César M. Jeveaux



Página(s): 2/5


No exemplo SimplesThread2 a classe Escrita implementa a interface Runnable. Qualquer classe que implementar a interface Runnable deve ter a descrição do método run().

class Escrita implements Runnable {

        private int i;

        public void run() {

               while(true)

                       System.out.println("Número: "+ i++);

        }

}


 

public class SimplesThread2 {

        public static void main(String[] args) {

               Escrita e = new Escrita();         //Cria o contexto de execução

               Thread t = new Thread(e);        //Cria a linha de execução

               t.start();                                     //Ativa a thread

        }

}



A classe SimplesThread2 cria o contexto de execução da thread no momento que cria uma instância de um objeto Runnable, que no caso é o objeto Escrita. 

Escrita e = new Escrita(); //Poderia ser Runnable e = new Escrita(); 

Para criar uma linha de execução, basta criar a thread, fornecendo o contexto (o local onde há o método run da thread). 

Thread t = new Thread(e); 

O início da thread propriamente dito ocorrerá com o método start().

Métodos para trabalhar com Threads



A classe Thread dispõe de vários métodos. Abaixo segue uma descrição resumida de alguns destes: 

7.Thread(...) – construtor da classe. Permite que seja instanciado um objeto do tipo Thread; 
8.void run() – Deve conter o código que se deseja executar, quando a thread estiver ativa; 
9.void start() – Inicia a thread. Ou seja, efetiva a chamada do método run(); 
10.void stop() – encerra a thread; 
11.static void sleep(long tempo) – deixa thread corrente inativa por no mínimo tempo milisegundos e promove outra thread. Note que este método é de classe e, conseqüentemente, uma thread não pode fazer outra thread dormir por um tempo; 
12.static void yield() – Deixa a thread em execução temporariamente inativa e, quando possível, promove outra thread de mesma prioridade ou maior; 
13.void suspend() – Coloca a thread no final da fila de sua prioridade e a deixa inativa (método deprecado); 
14.void resume() – Habilita novamente a execução da thread. Este método deve ser executado por outra thread, já que a thread suspensa não está sendo executada (método deprecado); 
15.void interrupt() – envia o pedido de interrupção de execução de uma thread; 
16.static boolena interrupted() – Verifica se a thread atual está interrompida; 
17.void join() – Aguarda outra thread para encerrar; 
18.boolean isAlive() – retorna true caso uma thread estiver no estado executável ou bloqueado. Nos demais retorna false; 
19.void setPriority(int prioridade) – Define a prioridade de execução de uma thread. Os valores de prioridade estão entre 1 e 10; 
20.int getPriority() – verifica a prioridade de execução de uma thread; 
21.synchronized – mecanismo que permite ao programador controlar threads, para que as mesmas possam compartilhar a mesma base de dados sem causar conflitos; 
22.void wait() – Interrompe a thread corrente e coloca a mesma na fila de espera (do objeto compartilhado) e aguarda que a mesma seja notificada. Este método somente pode ser chamado dentro de um método de sincronizado; 
23.void notify() – Notifica a próxima thread, aguardando na fila; 
24.void notifyAll() – Notifica todas as threads. 

Há também, vários métodos para trabalhar com agrupamentos de threads. A documentação necessária pode ser encontrada no JDK, no pacote Java.lang.ThreadGroup.

 
 

Entendendo melhor o uso de threads



O que acontece com a thread quando termina o método main? Porque o Garbage Collection não elimina a thread da memória, já que não há nenhuma referência para a mesma? O que ocorre é que o programa pode não ter uma referência explícita para a thread, mas implicitamente a thread está cadastrada na JV e continuará cadastrada enquanto não for encerrada. Desta forma, mesmo após executar o último comando do main, o programa permanece sendo executado. Para encerrá-lo, todas as referências implícitas do programa devem ser eliminadas. Este mesmo princípio ocorre para os componentes de uma interface gráfica, onde por exemplo, mesmo ao final do main um frame pode ficar ativo. Para visualizar melhor o uso de threads, o arquivo VariasThreads.java, apresenta um incremento da classe SimplesThread2. A classe Escrita passou a ter uma variável de instância que identifca a thread que está sendo executada. 


class Escrita implements Runnable {

        private int i;

        private static int cont = 0;

        private int identificacao;

        public void run() {

               while(true)

                       System.out.println("Número (" + identificacao + "): " + i++);

        }

        public Escrita() {

               cont++;

               identificacao = cont;

        }

}

 
 

public class VariasThreads {

        public static void main(String[] args) {

               Runnable r1 = new Escrita();

               Runnable r2 = new Escrita();

               New Thread(r1).start();

               New Thread(r2).start();

        }

}

 
 



A execução de uma thread nativa depende do S.O.. Embora a linguagem Java seja totalmente portável, certos cuidados tem que ser tomados para que as threads cooperam adequadamente, independente da JVM. Na verdade, o que se espera é que uma thread, após ser executada, passe a promover outras threads, mantendo a ordem de prioridade entre as mesmas. Se isto não ocorrer, alguns S.O. poderão ter as demais threads paradas durante a execução da thread "xxxxx". O programa VariasThreads2.java refaz a classe Escrita para, após exibir a mensagem na saída padrão, a thread ficar inativa por pelo menos 500 milisegundos. O método sleep(long tempo) faz com que a thread adormeça por tempo milisegundos e promove outras threads.

class Escrita implements Runnable {

        private int i;

        private static int cont = 0;

        private int identificacao;

        public void run() {

               while(true)

                       System.out.println("Número (" + identificacao + "): " + i++);

                       try {

                               Thread.sleep(500);

                       }

                       catch(InterruptedException e) {}

        }

        public Escrita() {

               cont++;

               identificacao = cont;

        }

}

public class VariasThreads {

        public static void main(String[] args) {

               New Thread(new Escrita()).start();

               New Thread(new Escrita()).start();

        }

}

Para analisar a segunda abordagem de implementação de threads, o programa MultiThread.java cria três threads com tempos de espera e nomes distintos. Para gerar um tempo de espera randômico foi utilizado o método Math.random().

 
 

class UmaThread extends Thread {

        private int delay;

        public UmaThread(String identifacacao, int delay) {

               super(identificacao);

               this.delay = delay;

        }

        public void run() {

               String identificação = this.getName();

               try {

                       sleep(delay);

               }

               catch(InterruptedException e) {

                       System.out.println("Thread: " + identificacao + " foi interrompida');

               }

               System.out.prinln(">>" + identificacao + " " + delay);

        }

}

 
 

public class MultiThread {

        public static void main(String[] args) {

               UmaThread t1,t2,t3;

               t1 = new UmaThread("Primeira", (int)(Math.random()*8000));

               t2 = new UmaThread("Segunda", (int)(Math.random()*8000));

               t3 = new UmaThread("Terceira", (int)(Math.random()*8000));

 
 

               t1.start();

t2.start();

t3.start();

        }

}



Leia mais em: Utilizando Threads - parte 1 http://www.devmedia.com.br/utilizando-threads-parte-1/4459#ixzz2CajFZBMG

Comentários

Postagens mais visitadas deste blog