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
.jpg)

Nenhum comentário:
Postar um comentário