Métodos e blocos sincronizados

Propaganda
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.
Download