Nome: Anderson Kenji Ono Daniel Botelho Luna João Paulo A. Vecchi Júnior César Rosante Sincronismo em Java As primitivas de sincronismo inerentes à linguagem Java[3] são poderosas para lidar com a maioria das interacões entre processos. Porém, são complicadas de se usar com programadores novatos. Por isso, apresenta-se uma forma melhor abstraída para se utilizar as primitivas e obter sincronismo entre processos: monitores, primitivas internas a linguagem e o mecanismo rendezvous[1]. Os mecanismos de sincronismos em Java usam basicamente o seguinte: - palavra chave synchonized - os métodos wait(), notify(), notifyAll(). Métodos e blocos sincronizados Sincronização de blocos pega um argumento de cada objeto a sincronizar. Essa prática habilita qualquer método a travar qualquer objeto. Esta é a forma mais comum de sincronizar blocos. synchronized void f() { /* body */ } equivale a: void f() { synchronized(this) { /* body */ } } A palavra chave synchonized nao é considerada parte da assinatura de método. Assim, o modificador de sincronismo não é automaticamente herdado pelas subclasses. Instancias de métodos sincronizados nas subclasses empregam a mesma trava como aquelas nas suas superclasses mas a sincronização num método interno da classe é independente da classe externa. Porem, um método não estático interno pode travar a classe que o contem , usando OuterClass: synchronized(OuterClass.this) { /* body */ } Monitores Da mesma forma que os objetos tem uma trava, eles têm um grupo-espera que é manipulado pelos métodos: - wait notify notifyAll Thread.interrupt As entidades que possuem travas e espera são chamadas monitores (outras linguagens podem defini-los de forma diferente). Qualquer objeto pode ser um monitor. O grupo-espera para cada objeto é mantido internamente pelo JVM (Java Virtual Machine). O grupo segura os processos bloqueados pelo wait até que as correspondentes notificações são invocadas ou a espera é de alguma forma liberada pelo sistema. Devido à forma pela qual os grupos interagem com o travamento, os métodos wait, notify e notifyAll podem ser invocados somente quando os processos alvo alcanca a barreira de sincronismo. Segue um breve resumo das primitivas: Wait A chamada do método wait resulta nas seguintes ações: 1- Se o thread corrente for interrompido, então o método finaliza imediatamente. Por outro lado, o thread corrente é bloqueado. 2- A máquina virtual Java coloca o thread em um conjunto de processos esperando associados com o objeto destino. Notify A chamada de notify resulta nas ações descritas a seguir: 1- Se é possível escolher arbitrariamente uma thread, digamos T, ela é removida pela máquina virtual do conjunto de processos que estão em espera e associados com o objeto destino. Não há garantias de qual thread será selecionada do conjunto que está esperando. 2- T deve obter novamente a sincronização de travamento para o objeto destino. Isso irá sempre ser causado ao bloco a menos que outro bloco obtenha o travamento primeiro. 3- T é então recomeçado a partir do ponto de sua espera. Notify All Notify All trabalha do mesmo modo que notify exceto na forma como os passos ocorrem(de fato, simultaneamente) para todos threads no conjunto de processos que estão em espera pelo objeto. No entanto, porque eles devem adquirir a trava, threads continuam um por vez. Timed Waits As versões timed dos métodos de espera, wait(long msecs) e wait(long msecs, int nanosecs), pegam argumentos especificando o máximo tempo desejado para o conjunto de processos na espera. Eles funcionam do mesmo modo como a versão do untimed exceto que se uma espera não foi notificada antes de seu tempo expirar, ele é reiniciado automaticamente. O método Thread.sleep(long msecs) usa uma espera timed, mas não interfere na barreira de sincronismo do objeto. Ela funciona como se estivesse assim definido: if (msecs != 0) { Object s = new Object(); synchronized(s) { s.wait(msecs); } } É claro que um sistema não precisa implementar o método espera exatamente do mesmo modo. Um outro método para garantir sincronização é descrito a seguir. O mecanismo rendezvous para Java Existe um mecanismo mais eficiente e fácil de se impor ordenação aos vários threads em um sistema. Este mecanismo é baseado no rendezvous que foi criado para simplificar a tarefa de multiprogramação. O resultado dessa implementação é uma abstração poderosa, fácil e eficiente. Esse mecanismo(rendezvous) é implementado como uma simples classe e provê um conjunto de funções que podem ser utilizados por muitas aplicações. Ele permite que possa ser sincronizado um número arbitrário de threads concorrentes muito facilmente. Para usar a classe rendezvous, os métodos usados são add(), remove() e rendezvous(). Primeiro, um novo objeto rendezvous e novos objetos threads a serem sincronizados são criados e adicionados em rendezvous através do método add() antes de novos threads serem startados. Com o corpo de cada thread o método rendezvous é chamado. Isso faz com que aquela thread fique bloqueada até que todas as threads naquela particular rendezvous tenham chamado o método. Quando todas as threads tiverem feito isso, elas terminam sua execução. Efeitos do mecanismo rendezvous Adicionalmente, em qualquer ponto durante sua execução, uma thread pode decidir remover a si mesma ou outra thread do rendezvous. Esta característica suporta a implementação de condições de sincronização, as quais podem ser usadas em diversas aplicações. Além disso, muitos objetos rendezvous podem existir com um programa apenas e uma simples thread pode participar em muitas rendezvous. Isso permite diferentes "clocks" coexistirem com um mesmo programa e também permite a existência de expressões de comportamento que são relativos ao paradigma de orientação ao objeto. A classe rendezvous é portanto útil para interações entre threads. A implementação do mecanismo rendezvous requer algumas primitivas da linguagem, mas as usa mais eficientemente. A chamada ao método NotifyAll() é feita apenas a cada sinal, e o método wait() a cada sinal de cada thread. O rendezvous não faz chamadas externas a Notify() e NotifyAll() não gerando overhead do sistema operacional. Os programas também ficam mais limpos e elegantes usando o método rendezvous. Referências 1. C. A. R. Hoare, "Communicating Sequential Processes," Communications of the ACM, vol. 21, no. 8, pp. 666-677, 1978. 2. G. Berry and S. Ramesh, "Communicating Reactive Processes," Proc. 20th ACM Conference on Programming Languages, pp. 85-98, 1993. 3. J. Gosling, B. Joy, and G. Steele, The Java Language Specification. Reading, MA: Addison-Wesley, 1996. 4. Tanenbaum, Andrew S., "Sistemas Operacionais Modernos", 1992.