terça-feira, 3 de setembro de 2013

THREADS



A maioria dos sistemas operacionais 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 a sua execução. A esta capacidade é denominado o termo multiprocessamento(multithreading).

Teoricamente existe uma grande proteção para que um processo não afete a execução de outro, modificando. Este alto grau de isolamento reduz os desagradáveis GPF's (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 oferecida pelos construtores de sistemas operacionais foi o uso de Threads. 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 da 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:
  • A utilização de mecanismos nativos de operação de S.O.
  • Aimplementação completa da operação de thread na JVM. 
A diferença básica é que as threads com mecanismos nativos do S.O. são mais rápidas. Em contrapartida, as implementadas pela JVM têm independência completa de plataforma.
O suporte a múltiplas linhas de execução permite que múltiplos processamentos ocorram em "paralelo" (em computadores com um processador, os threads não são executados em paralelo e, sim, concorrentemente).



Existe duas formas de utilização de Threads em JAVA, a primeira forma é a mais simples de todas e consiste em criar uma classe que herde de java.lang.Thread. Uma vez criada esta classe, basta criar um objeto da mesma e chamar o método start.
Este método registrará a thread no escalonador.
A segunda forma e implementando uma interface da classe Runnable, quando não ser conveniente criar uma subclasse de Thread ou não ser possivel devido ao mecanismo de herança implementado em Java.
Em Runnable, não mais utilizamos uma subclasse de java.lang.Thread. Ao invés disso, utilizamos um objeto da própria classe java.lang.Thread. No entanto definimos, através do construtor da classe, que código a thread deveria executar. O objeto que é passado como argumento para o construtor deve implementar a interface Runnable. Dessa forma, a thread irá chamar o método run do objeto passado como parâmetro no momento que ela for autorizada a utilizar o processador. A classe Imprime implementa a interface Runnable. Se tivéssemos utilizado o construtor vazio da classe Thread, o código compilaria normalmente. Mas ao invés de executar o método run da classe Imprime, a thread executaria o método run da classe Thread (que não faz nada).

public class jThreads
{
    public jThreads() {
    }
   
    public static void main(String[] args) {
            Thread threadComHeranca = new Tarefa1();
Thread threadComRunnable = new Thread(new Tarefa2()); // no caso de Runnable é obrigatório utilizar new Thread()
            threadComHeranca.start();
            threadComRunnable.start();
    }
}
class Tarefa1 extends Thread {
            // Thread utilizando herança estendendo a classe Thread
            public void run() {
                        for(int i=0; i<1000; i++){
                        // sleep (milseg) coloca a thread em estado bloqueado
                                   try {
                                               sleep(100);
                                    } catch (InterruptedException e) { }
                                   System.out.println("Usando herança");
                        }
            }
}
class Tarefa2 implements Runnable {
            // Thread utilizando implementação da interface Runnable
            public void run() {
                        for(int i=0; i<1000; i++){
                                   try {
                  Thread.sleep(100); // no caso de Runnable é obrigatorio utilizar Thread.sleep()
            } catch (InterruptedException e) { }
                                   System.out.println("Usando Runnable");
                        }
            }
}



O ciclo de vida de uma thread tem cinco estados básicos:

  • 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 possa ser executada.
  • 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.
  • Bloqueado: Quando for desativada. Para desativar uma thread é necessário que ocorra uma das quatro operações a seguir:
    • Foi chamado o método suspend() da thread;
    • A thread chamou os algum dos medotos:
      • Sleep(millseg) – Aguarda xx miliseg para executar prox.linha
      • Wait() – Fica aguardando um notify()
      • Yield() – Pausa e da passagem a outras thread com mesma prioridade
    • 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 ações deve ocorrer, em oposição às ações acima:
      • Retorna após o tempo especificado, caso a thread estiver adormecida;
      • Retorna através do método resume(), caso a thread tiver sido suspensa;
      • Retorna com método notify() (ou notifyAll()), caso a thread estiver em espera;
      • Retorna após a conclusão da operação de I/O.
  • Encerrado
    Quando encerrar a sua execução e, isto pode ocorrer pelo término do método run(), ou pela chamada explícita do método stop().
    Nunca o método run deve ser chamado diretamente. Isto é feito pelo método start da Thread. Ao terminar a execução de uma thread, ou seja quando o método run é finalizado, a thread é considerada morta não podendo portanto ser reiniciada. O objeto que representa a thread ainda existe logo pode-se invocar qualquer um de seus métodos com exceção do método start.


Java cria a ilusão de que várias linhas estão em execução simultaneamente, alternando rapidamente o controle do processador entre as linhas. Dependendo do propósito do programa, poderá haver ocasiões em que seja necessário dar preferência ao processamento de determinadas Threads.
As threads sempre iniciam sua execução com a prioridade herdada da superclasse, que por default é igual a 5 (Thread.NORM_PRIORITY). Porém, o programador pode alterar a prioridade da thread como convier, dentro do range de prioridades:
Thread.MIN_PRIORITY  [1]
Thread.MAX_PRIORITY [10]
Thread.NORM_PRIORITY [5]

Escalonamento mostra os mecanismos que determinam como os threads executáveis (runnable) irão utilizar tempo de CPU, que pode ser alterada com setPriority(int p) e pode ser lida com getPriority().
  
Sincronismo entre threads

A primeira forma de sincronização: SYNCHONIZED

Dentro de um programa, segmentos de código que acessam os mesmos dados usando threads diferentes e concorrentes são chamados de regiões críticas ou seções críticas e podem ser um bloco de statements ou um método inteiro e deve ser identificada com a palavra synchronized.

public class CubbyHole {
    private int contents;
    private boolean available = false;

    public synchronized int get(int who) {
    } 
    public synchronized void put(int who, int value) {
    }
}

Ao entrar num método synchronized, o thread que chamou o método trava o objeto cujo método foi chamado, se outro thread chamar um método synchronized do mesmo objeto, ele vai esperar até o primeiro liberar o lock.

Segunda forma de sincronização: NOTIFYALL, NOTIFY e WAIT

public final void notify( )
Acorda uma única thread na fila de espera   

public final void notifyAll( )
Acorda todas as threads na fila de espera

public final void wait( ) 

A thread espera (dorme) a notificação de uma outra thread. Quando notificada, a thread deve ganhar novamente o lock  associado com o objeto  

Nenhum comentário:

Postar um comentário