Sincronizar ou gerenciar a fila de threads?

Olá a todos
O q é melhor: eu utilizar os métodos wait() e notify() para controlar as threads q acessarão o meu método, ou escrever blocos synchronized {} nas instruções críticas? Quais situações seriam adequadas para as duas maneiras?
Abraços

:scrambleup: Velho, para saber o que é melhor, só sabendo mesmo o que você quer fazer. Se não me engano, blocos synchronized deixam apenas uma Thread por vez acessar um objeto. Cuidado com alguns pequenos detalhes ao utilizá-lo:
:arrow: Não ponha dentro de método sincronizado um bloco desses bloqueando o próprio objeto (this) - fiz isso sem querer e me levou um dia pra ver a besteira…
:arrow: Não lembro de outros

Se você usar wait() também é legal, te dá um bom controle sobre quantqw Threads por vez podem acessar alguma coisa… Dá pra fazer um controlador usando as duas coisas ao mesmo tempo… Depende do seu problema… A gama de usos é muito ampla.

Valeu cara. Perguntei isso pq pensei q colocar blocos synchronized dentro dos métodos fosse melhor do q ficar procurando posições estratégicas pra alocar chamadas a notify(), notifyAll() ou wait().
Abraços

Os métodos wait, notify e notifyAll SÓ podem ser chamados dentro de blocos ou métodos synchronized

Quando uma thread chama um método synchronized ela obtém o lock do objeto alvo da chamada…se outra thread tentar executar um método synchronized deste mesmo objeto, ela vai ficar tentando obter o lock do objeto até conseguir…para que ela pare de tentar (se não me engano, dessa forma liberando processamento e evitando a troca de contexto entre as threads), você manda que ela wait…quando a primeira thread não precisar mais do lock do objeto (quando tiver saído da sessão crítica), ela libera as threads em espera por meio de notify ou notifyAll

Ou seja: é importante usar os dois recursos de forma combinada :wink:

[]'s

Na verdade é quase isto.
Somente a Thread que detém o lock de um objeto pode chamar wait, notify ou notifyAll.
Portanto quando uma Thread chama wait ela está abdicando da propriedade do lock, dando a outras Threads a chance de obter o lock no objeto. Portanto ela se torna uma waiting-thread.
Quando alguma outra Thread invocar notify ou notifyAll no locking-object a JVM irá escolher uma das waiting-threads para entrar novamente em execução (running-state). Isso só irá acontecer quando a thread dona do lock liberá-lo, assim:

       syncronized( mutex ){
            // faz um monte de coisas.
            mutex.notifyAll();
            // pode fazer um outro monte de coisas...
       } // neste ponto a thread corrente pode sofrer preempção e a outra que foi escolhida pela JVM entrar em execução, isso depende do nível de prioridade.

synchronized e wait/notify são primitivas diferentes de concorrencia, logo seu uso é distinto.

Use synchronized para introduzir exclusão mútua e wait/notify para condições.

Pois é, formulei mal minha pergunta: usar chamadas wait(), notify(), notifyAll() dentro de métodos synchronized ou colocar blocos synchronized dentro dos métodos, somente nas instruões críticas (como gravar em um arquivo)?
Obrigado pela atenção de todos.

É um bocado complicado usar direito sincronização e locks em Java. Seria mais interessante você pensar exatamente o que você quer fazer, de uma forma de nível mais alto, e usar a biblioteca util.concurrent (se estiver usando JDK 1.2 até 1.4) ou java.util.concurrent (se estiver usando JDK 5.0). Essa biblioteca tem um monte de coisas interessantes, como thread pools, controle de acesso a recursos e outras coisas que não são fáceis de escrever direito em Java. Ela é usada no JBoss, e acho que ela é uma das grandes responsáveis pelo bom desempenho desse servidor de aplicações. Vale a pena usar essa biblioteca, porque problemas com locks e outras coisas são muito, muito difíceis de debugar direito.

A biblioteca util.concurrent pode ser baixada de:

http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html

e foi escrita por Doug Lea, o mesmo que escreveu (praticamente sozinho) as novas classes java.util.concurrent do JDK 5.0.

Realmente, vai depender muito do que você quer fazer. Por exemplo, se você quer apenas garantir que uma determinada variável é acessada apenas por uma Thread por vez, basta colocar todos os trechos de código em blocos synchronized:

synchronized(this) {
    // seu código que acessa a variável aqui
}