2 dúvidas sobre Threads

16 respostas
T
  1. Ao sincronizar um método ou um bloco qualquer com a palavra chave syncronized, como esse bloco é liberado? Usando um notify() implícito? Ou um notifyAll() implícito?

Por exemplo, dado o código:

syncronized(this){ //codigos aqui }

Quando ele acaba de executar, ele faz o que exatamente para avisar outras threads que eventualmente estejam esperando a liberação desse bloco? Eu acho que ele faz algo assim:

syncronized(this){ //codigos aqui notifyAll(); }

só que implicitamente…


  1. Imagina que eu tenho o seguinte código:

syncronized(this){ //codigos de região crítica aqui notifyAll(); //mais código de região crítica aqui }

Ao executar o notifyAll() no meio do código, ele libera o lock do objeto this, correto? Isso quer dizer que a parte marcada com " //mais código de região crítica aqui" poderá executar sem sincronismo? Ou seja, mesmo que esteja dentro do bloco syncronized, ao chamar o notifyAll() quebramos o sincronismo, não? Ou o código marcado como " //mais código de região crítica aqui" nem é executado? (como uma exceção sendo lançada ali). O que acontece exatamente ao chamarmos notifyAll() ou notify() no meio de um bloco syncronized?

Obrigado!!! :slight_smile:

16 Respostas

V

eu acho que quando vc tem o seguinte código:

syncronized(this){   
   //codigos de região crítica aqui   
   notifyAll();   
  //mais código de região crítica aqui   
}

ele só libera o lock quando sair do bloco sincronizado…

robson_oliveira

Bom dia!

Quando sicronizamos um metodo qualquer, isso detona a capacidade de processo simultaneo, ou seja, somente um thread de cada vez podera executar o metodo.

A liberação do lock acontecera somente depois que a thread que esta executando o metodo sair deste metodo sicronizado, e este metodo so estara livre ate que outra Thread o execute.

Quando você chamar o metodo notify(), isso nao significa que o Thread abrira mao do seu bloqueio na mesma hora, se a Thread ainda estiver concluido o codigo sicronizado, o bloqueio não sera liberado ate que ele saia do codigo.

Quando você chamar o metodo notifyAll(), isso significa que você estara notificando todos os threads que estiverem esperando por um objeto especifico, isso fara com que os Threads, saiam do estado de espera(A Thread ainda esta ativo mas nao qualificado para execução) e retornem ao estado “executavel”, que o estado em que uma thread se encontra qualificada para ser executada.

rmala_ti

vmsb11:
eu acho que quando vc tem o seguinte código:

syncronized(this){   
   //codigos de região crítica aqui   
   notifyAll();   
  //mais código de região crítica aqui   
}

ele só libera o lock quando sair do bloco sincronizado…

Concordo!

rmala_ti

robson oliveira:
Bom dia!

Quando sicronizamos um metodo qualquer, isso detona a capacidade de processo simultaneo, ou seja, somente um thread de cada vez podera executar o metodo.

A liberação do lock acontecera somente depois que a thread que esta executando o metodo sair deste metodo sicronizado, e este metodo so estara livre ate que outra Thread o execute.

Quando você chamar o metodo notify(), isso nao significa que o Thread abrira mao do seu bloqueio na mesma hora, se a Thread ainda estiver concluido o codigo sicronizado, o bloqueio não sera liberado ate que ele saia do codigo.

Quando você chamar o metodo notifyAll(), isso significa que você estara notificando todos os threads que estiverem esperando por um objeto especifico, isso fara com que os Threads, saiam do estado de espera(A Thread ainda esta ativo mas nao qualificado para execução) e retornem ao estado “executavel”, que o estado em que uma thread se encontra qualificada para ser executada.

Certo, é isso que eu entendi também.

Mas uma Thread vai para o estado de espera, somente quando chamar o método wait() no objeto que tiver o bloqueio ou tem outra forma?

ViniGodoy

Ela também vai para estado de espera se chegar num bloco sincronizado, e outra thread estiver dentro. O wait é só para pedir que a thread que já está no bloco entre em espera também.

ViniGodoy

Um notifyAll() implícito. Assim que a thread sai do bloco sincronizado, é dada uma chance de processamento a todas as threads que estão aguardando para entrar nesse bloco. A primeira que acordar e entrar no bloco, leva. As demais, voltam a aguardar.

L

Quando você dá um notify()/notifyAll() num bloco sincronizado, você apenas diz para aquelas threads bloqueadas com wait() de que pode voltar ao estado de espera. O bloco atual onde você deu notify() não sai de sincronismo (apesar da JVM, se quiser, reeschedular uma outra thread no período), o lock é da thread até sair do bloco.

Porém, não existe um notify()/notifyAll() implícito. Se não existe no seu código nenhum ponto que dê notifyAll() para um dado objeto, quando você der um wait(), fica preso pra sempre (dead-lock), mesmo que haja um outro core que tenha acabado de sair de um bloco sincronizado.

T

rmalati:
vmsb11:
eu acho que quando vc tem o seguinte código:

syncronized(this){   
   //codigos de região crítica aqui   
   notifyAll();   
  //mais código de região crítica aqui   
}

ele só libera o lock quando sair do bloco sincronizado…

Concordo!

Mas então não existe utilidade nenhuma em dar um notify() ou um notifyAll() dentro de um bloco sincronizado?

L

Existe sim, quem se bloqueu com wait(), só sai com uma chamada de notify()/notifyAll() de outra thread. Não existe nada de implícito.

T

Existe sim, quem se bloqueu com wait(), só sai com uma chamada de notify()/notifyAll() de outra thread. Não existe nada de implícito.

Humm, entendi a sua explicação. Mas, voltando à minha dúvida inicial, acho que eu não fui muito claro na pergunta (ou talvez eu tenha entendido errado).

Vamos supor que temos 2 threads (A e B) e que a seguinte situação ocorra:

  1. A thread ‘A’ entra no bloco syncronized.
  2. Enquanto a thread ‘A’ ainda está no bloco syncronized, a thread ‘B’ também tenta entrar nesse bloco (mas, obviamente, não consegue pois a thread ‘A’ está nele (a thread ‘A’ obteve o look)). Assim, a thread ‘B’ fica travada alí, como se a thread ‘B’ tivesse executado um wait() implícito (me corrija se eu estiver errado). Ou seja, a thread ‘B’ fica ali parada, esperando a thread ‘A’ liberar o lock.
  3. Quando a thread ‘A’ finalmente sai do bloco synchronized, a thread ‘B’ deve ser avisada de que a thread ‘A’ saiu, correto? (senão a thread ‘B’ vai ficar ali parada, esperando pra sempre). Como ocorre esse aviso? É essa a minha dúvida.
T

Ela também vai para estado de espera se chegar num bloco sincronizado, e outra thread estiver dentro. O wait é só para pedir que a thread que já está no bloco entre em espera também.

ViniGodoy, deixa eu ver se eu entendi. Imagina que temos 2 threads, A e B. A thread A entra no bloco syncronized. Daí, logo após, a thread B tenta entrar, mas não consegue (pois A já está no bloco syncronized). Daí, se a thread A chegar em um lugar do meu bloco syncronized onde eu dou um notifyAll() explícito, essa thread B:

  1. Sai do estado de espera
  2. Tenta entrar novamente no bloco syncronized
  3. A thread B vê que o lock daquele bloco ainda não foi liberado. Assim, ela volta ao estado “wait”, sem entrar no bloco syncronized.

É isso?

ViniGodoy

TiagoTC:
ViniGodoy, deixa eu ver se eu entendi. Imagina que temos 2 threads, A e B. A thread A entra no bloco syncronized. Daí, logo após, a thread B tenta entrar, mas não consegue (pois A já está no bloco syncronized). Daí, se a thread A chegar em um lugar do meu bloco syncronized onde eu dou um notifyAll() explícito, essa thread B:

  1. Sai do estado de espera
  2. Tenta entrar novamente no bloco syncronized
  3. A thread B vê que o lock daquele bloco ainda não foi liberado. Assim, ela volta ao estado “wait”, sem entrar no bloco syncronized.

Veja bem, existem dois estados possíveis, ambos de threads “paradas”:

BLOCKED: Threads esse estado estão numa fila, esperando que o lock seja liberado para entrar no bloco sincronizado;
WAITING: Threads nesse estado estão em outra fila, e dormiram dentro de um wait.

O notifyAll() vai acordar todas as threads no estado WAITING. O que não é o caso do exemplo que você citou.

O que eu falei sobre um “notifyAll()” implícito, é que quando deixa o bloco sincronizado, todas as threads que estão no estado blocked sofrem uma espécie de “notifyAll()”, e irão tentar entrar no bloco sincronizado.

T

ViniGodoy:
TiagoTC:
ViniGodoy, deixa eu ver se eu entendi. Imagina que temos 2 threads, A e B. A thread A entra no bloco syncronized. Daí, logo após, a thread B tenta entrar, mas não consegue (pois A já está no bloco syncronized). Daí, se a thread A chegar em um lugar do meu bloco syncronized onde eu dou um notifyAll() explícito, essa thread B:

  1. Sai do estado de espera
  2. Tenta entrar novamente no bloco syncronized
  3. A thread B vê que o lock daquele bloco ainda não foi liberado. Assim, ela volta ao estado “wait”, sem entrar no bloco syncronized.

Veja bem, existem dois estados possíveis, ambos de threads “paradas”:

BLOCKED: Threads esse estado estão numa fila, esperando que o lock seja liberado para entrar no bloco sincronizado;
WAITING: Threads nesse estado estão em outra fila, e dormiram dentro de um wait.

O notifyAll() vai acordar todas as threads no estado WAITING. O que não é o caso do exemplo que você citou.

O que eu falei sobre um “notifyAll()” implícito, é que quando deixa o bloco sincronizado, todas as threads que estão no estado blocked sofrem uma espécie de “notifyAll()”, e irão tentar entrar no bloco sincronizado.

Perfeito! :smiley: Deixa eu perguntar outra coisa. A única maneira de uma thread entrar no estado “blocked” é tentando entrar em um bloco syncronized (supondo que este bloco já esteja sendo usado por outra thread, claro) ? E a única maneira de uma thread entrar no estado “wait” é chamando o wait explicitamente? Existem outras formas?

Obrigado! :slight_smile:

ViniGodoy

Ela só entra em BLOCK caso esteja esperando por um bloco sincronizado ou se realizar operações de I/O.

Ela entra em WAIT se:

  1. Der um wait() sem timeout;
  2. Der um join() sem timeout;
  3. Chamar o método park() da classe LockSupport (esse não cai na certificação).

Ela entra em TIMED_WAIT se:

  1. Der um wait() com timeout;
  2. Der um join() com timeout;
  3. Der um sleep();
  4. Chamar os métodos parkNanos() ou parkUntil(), da classe LockSupport (também não caem na certificação).

Note que um notify() ou notifyAll() só irá acordar threads que dormiram com o wait(), porque esses comandos atuam sobre as threads submetidas à um mesmo monitor. No caso do join() ou do sleep() um monitor separado é utilizado.

T

ViniGodoy:
Ela só entra em BLOCK caso esteja esperando por um bloco sincronizado ou se realizar operações de I/O.

Ela entra em WAIT se:

  1. Der um wait() sem timeout;
  2. Der um join() sem timeout;
  3. Chamar o método park() da classe LockSupport (esse não cai na certificação).

Ela entra em TIMED_WAIT se:

  1. Der um wait() com timeout;
  2. Der um join() com timeout;
  3. Der um sleep();
  4. Chamar os métodos parkNanos() ou parkUntil(), da classe LockSupport (também não caem na certificação).

Note que um notify() ou notifyAll() só irá acordar threads que dormiram com o wait(), porque esses comandos atuam sobre as threads submetidas à um mesmo monitor. No caso do join() ou do sleep() um monitor separado é utilizado.

Existe alguma maneira de sair de um join() sem ter que esperar a thread terminar? Porque você disse que o notify() e o notifyAll() apenas servem para acordar as threads que estão no estado WAIT pela chamada de wait().

ViniGodoy

Sim, chamando interrupt(), sobre a Thread em questão.
O método join() então abortaria e dispararia uma InterruptedException.

Outra forma é usando o join() com timeout. Aí ele sai naturalmente, assim que o timeout acaba.

Criado 16 de janeiro de 2010
Ultima resposta 19 de jan. de 2010
Respostas 16
Participantes 6