terça-feira, 25 de agosto de 2009

Entendendo Threads 03: Comunicação entre threads

Comunicação entre threads:

Sumario: yield(),wait(), notify(), notifyAll()


Método de comunicação entre escalonador threads e threads threads:


YIELD:

package: java.lang.Thread;

static void yield() - yield(cede) , Yield da uma chance para que outra thread de mesma prioridade que esta no estado RUNNABLE possa se tornar RUNNING. Se nenhuma outra thread de mesma prioridade (Porque de mesma prioridade? Porque se for de menor prioridade ela nem entrará mesmo, e se for de maior, com ou sem yield() ela entrara) estiver no pool de RUNNABLE, então nada acontecerá.


WAIT E NOTIFY:
package: java.lang.Object;

São métodos do OBJETO, que ajudarão na comunicação de threads.

- Principio básico sobre wait() e notify():

1 - thread A necessita de uma certa condição e assume que thread B fará para ela.

2 - Quando thread B termina a condição ela notifica thread A, porem, isso não quer dizer que thread A será a thread RUNNING, continuara com B.

- Eles são preservadores de recursos, Vamos a um exemplo:

***Supondo que temos um E-commerce porem temos somente 1 produto para vender no momento, o produto é tao bom que 400 consumidores querem o produto(mais so temos 1 em estoque) - Sendo produto 1 thread, e cada consumidor 1 thread(ou seja 400 threads). Faz mais sentido eu deixar 399 threads em CPU intensive( ou seja em um loop procurando por threads produto ate achar) ou simplesmente eu deixo todas as threads consumidores(399) em uma fila de espera chamada: Bloqueado em pool de espera do objeto, onde quando houver mais 1 thread produto eu notifico elas, consumindo mais uma da fila e assim por vez!!!!????


Abstraindo wait e notify x I/O intensive:

Conversa entre Pessoas:

Ja esteve no carro um dia com seu filho, e ele ficava perguntando:

- Pai ja chegou?
- Não!
e de novo ele pergunta
- Pai ja chegou?
e de novo você responde:
- Não!
e de novo ele pergunta
- Pai ja chegou?
e de novo você responde:
- Não!

e de novo ele pergunta
- Pai ja chegou?

e você fica bravo com tantas perguntas e diz, fica ESPERANDO que quando chegar eu te aviso!

*** Basicamente deixa a thread em um bloqueio de stack, porque tem uma frame(a frame referente ao wait()) que bloqueia o top da stack por um determinado tempo ou notificação que nem read(), readLine() fazem. - So que essa frame normalmente esta em um bloco sincronizado, então a frame bloqueada, é a anterior a ela por causa da necessidade de verificação do synchronized.

void wait() - Aguarda até que uma condição ocorra. Este é um método da classe Objeto e deve ser chamado DE DENTRO DE UM METODO OU BLOCO SINCRONIZADO.

void notify() - Notifica uma thread que estava esperando, de que a condição ja ocorreu(Somente notifica, deixando assim a thread que estava esperando a condição no ESTADO "BLOQUEIO EM POOL DE BLOQUEIO DE OBJETO", explicando, o método/bloco é synchronized e a thread que invocou notify() ja é a thread com a chave do objeto, então a outra é notificada mais fica no "BLOQUEIO EM POOL DE BLOQUEIO DE OBJETO" POIS ela não tem a chave do objeto. Este é um método da classe Objeto e deve ser chamado DE DENTRO DE UM METODO OU BLOCO SINCRONIZADO.

passo a passo da explicação acima:


thread A e B compartilham o mesmo Objeto:

- thread A invoca myObject.wait() entrando assim no estado "Bloqueado em pool de espera do objeto"
-
thread B esta no estado Running agora
-
thread B entra em um bloco SINCRONIZADO e invoca myObject.notify()

- thread A entra no estado "Bloqueado em pool de bloqueio de objeto"

- thread B sai do BLOCO SINCRONIZADO e Thread.sleep(100)

- thread B entra no estado Bloqueado

- thread A agora entra no estado Executável, existe a avaliação do Escalonador

- thread A agora entra em estado EM EXECUÇÃO


Qual é o proposito de wait e notify?

É um mecanismo sincronizado , cada objeto em java tem(Lembre-se que qualquer classe is a Object), POREM é um mecanismo de COMUNICAÇÃO. Deixa uma thread aguardando por uma condição, e outra avisa quando ocorrer.


Oque acontece com a thread na qual um objeto invoca wait() ?

Ela vai para um POOL, chamado "BLOQUEIO EM POOL DE ESPERA DO OBJETO", E so sai de la, Quando a Thread que invocou notify() for bloqueada ou sair do bloco SINCRONIZADO, ja que as 2 são do mesmo objeto e em blocos sincronizados o acesso ao bloco so pode ser dado a uma thread por vez.

Quem notify para o wait?

Claro o mesmo objeto que aguarda, será na outra stack(thread) uma referencia que notifica.(Se na Stack A, objeto1 invoca wait(), na Stack B o mesmo objeto1 deve ter uma referencia la para invocar notify()). LEMBRE-SE POREM O OBJETO tem que ser Runnable de ambas as threads, ou fazer referencia em ambas.


Que condição preciso ter para invocar wait ou notify?

O método ou bloco que invocar wait() ou notify() deve ser synchronized, para evitar o round-robin(claro e o timeSlicing).

Noque devo me atentar?

A invocação de notify() só tira uma thread do estado "BLOQUEIO EM POOL DE ESPERA DO OBJETO" porem , COMO A THREAD QUE invocou notify() esta em um bloco sincronizado do mesmo objeto, a thread que estava wait() vai para o estado "BLOQUEIO EM POOL DE BLOQUEIO DE OBJETO" por falta da chave para acesso...E é importante também ter cuidado ao usar SLEEP() etc, pois pode ocorrer da thread ir dormir com a chave...Então de preferencia a usar wait() e notify() em blocos sincronizados.


Alguma consideração a mais?

Alem de não invocar sleep() em blocos sincronizados(Pois a thread pode ir dormir com a chave) por isso prefira wait() e notify(), Use comandos de bloqueio como sleep() etc no código da thread que dará a chance a outra para entrar, POREM CLARO em blocos não sincronizados.


Quem realmente fica em wait()?

Como eles estão em bloco sincronizado, a instrução que invocou que fica, pois é necessária a verificação novamente, PORQUE uma thread que estava no "BLOQUEADO EM POOL DE ESPERA DO OBJETO" quando sai de la entra no estado "BLOQUEADO EM POOL DE BLOQUEIO DE OBJETO" a qual so sai de la quando, a thread que esta com a chave liberar.

Exemplo:

public void run(){

for(int i = 0; i<>

myJob.get();

}

}

Esse método run() é de uma thread e abaixo temos a implementação do método que é o RUNNABLE da thread(ou seja o Job dela).


public synchronized void get(){

if(!flag){

try {

wait();

} catch (InterruptedException ex) {

Logger.getLogger(MyJob.class.getName()).log(Level.SEVERE, null, ex);

}

}//end of if


System.out.println("Get: "+dados);

flag = false;


CASO A FLAG SEJA false logicamente entra no bloco if, e a instrução wait(); da o bloqueio em myJob.get() - (Claro a instrução wait() esta dentro do bloco if, porem o bloqueio é na invocação); o contador(pc do PC Register) esta sabendo certinho o valor de i...É como começar a invocação do ponto onde terminou.

- Pois bem a thread no wait() entrou em Bloqueado em pool de espera do objeto, quando o objeto em outra thread deu notify() essa thread foi para Bloqueado em pool de bloqueio de objeto(Pois a chave do cadeado do objeto estava com a outra thread.). Quando a outra thread saiu do bloco/método sincronizado a chave foi liberada e daí sim a outra thread que estava bloqueada pode entrar na instrução.


Oque é flag?

Flag é como se fosse um demarcador, ex:

boolean test = true;


while(test)

{

i++;

Faz a demarcação, É como se fosse um verificador periódico; Ou apenas um teste de se CASO OCORRA.


NOTIFYALL -

Simplesmente notifica todas as threads que estão no Bloqueado em pool de espera do objeto" REFERENTES AQUELE OBJETO.. Muito cuidado ao usar. Lembre-se também que deve ser invocado de dentro de um método/bloco sincronizado.

Dicas:


- static void yield() - yield(cede) , Yield da uma chance para que outra thread de mesma prioridade que esta no estado RUNNABLE possa se tornar RUNNING.


- Agora temos os estados:

Clique na Imagem para maximizar


* Novo - Quando você instancia um objeto thread

* Executável - start() em um novo objeto thread ou quando volta da maioria dos estados

* Bloqueado - Thread.sleep()

* Bloqueado em pool de bloqueio de objeto - quando uma thread tenta entrar em um método/bloco sincronizado porem outra thread ja esta usando(ou seja fecho o acesso) então não temos a chave, ela fica nesse estado

* Bloqueado em pool de espera do objeto - Quando é dado wait() em um objeto a thread fica nesse estado

* Inativo - Dead é quando a thread terminou o code_block de run() seja normalmente ou prematuramente

* Em execução - É a thread que esta sendo executada(Usando A CPU) no real momento.


- wait() - Object.wait() Faz com que uma thread fique no estado - Bloqueado em pool de espera do objeto - Aguardando pela chamada de notify() do mesmo objeto...Object.notify().

- notify() - Tira uma thread do estado - Bloqueado em pool de espera do objeto -e faz com que ela fique no estado - * Bloqueado em pool de bloqueio de objeto - Pois o método/bloco é sincronizado e a thread que invocou notify() ainda esta nométodo/bloco fazendo com que a outra fique em aguardo no novo estado.

- De preferência a uso de flags em seu código.

- Ao usar métodos/blocos sincronizados de preferência a invocação de wait() e notify() ja que invocações como sleep() podem causar danos como: Uma thread ir dormir com a chave do OBJETO e outra thread quer acessar o método/bloco porem não pode. Então cuidado ao usar Thread.sleep() em contexto sincronizado.

- Para evitar possíveis problemas de obtenção e liberação das chaves dos cadeados dos Objetos, Libere as chaves na ordem Inversa na qual você obteve.

- Sempre divida tarefas em threads, melhora o uso da CPU e a distribuição do trabalho.

- Alguns métodos como read(), readLine() etc, deixam a Stack da thread atual noque chamamos de I/O intensive, a Stack fica realmente parada esperando alguma I/O.


PARA UTILITÁRIOS SOBRE THREAD, BUSQUE INFORMAÇÕES NO PACOTE

java.util.concurrent; -> inclusive existem classes que facilitam a criação de threads e inclusive o manuseio.



Nenhum comentário:

Postar um comentário