Muita calma nessa hora.
Vamos primeiro identificar as coisas. Existe:
- As threads que percorrem o código em objetos. Elas são postar para dormir (bloqueadas) e são elas que precisamos sincronizar;
- Um objeto chamado de “monitor” que controla a sincronização;
- A região crítica, que é o trecho de código que não queremos que duas threads compartilhem. Ele pode ou não ser na mesma classe do monitor;
Quando uma thread tenta entrar num código sincronizado, ela primeiro irá verificar se o monitor não está sendo usado por ninguém. Se não estiver, então ela entra na região, usando o monitor.
A partir desse momento, qualquer thread que tentar entrar nessa região crítica, não terá mais acesso ao monitor, pois ele está sendo usado pela primeira thread.
Uma analogia comum é imaginar que o monitor é a tranca de um banheiro de avião.
Quando a thread entra no bloco sincronizado (o banheiro), ela obtém o monitor (“tranca o banheiro”), e nenhuma thread poderá entrar lá (“estará ligado o sinal de OCUPADO”).
Pois bem… o que ocorre é que você tem que delimitar quais são os trechos que podem ou não ser compartilhados. E como você faz isso? Você põe a placa de ocupado sobre todos os trechos, ou seja, sincroniza os trecho que você não quer que multiplas threads percorram com o mesmo monitor.
Pois bem, agora, uma classe assim:
[code]public class Classe {
//Um int[0] é um objeto.
private int[] tranca = new tranca[0];
public void metodo1() {
//Faz qualquer coisa
}
public void metodo2() {
synchronized (tranca) {
//Faz outra coisa
}
}
public void metodo3() {
synchronized (tranca) {
//Faz outra coisa
}
}
[/code]
Tem apenas sinais de “OCUPADO” sobre os métodos 2 e 3. E não no método 1. Portanto, uma thread que entre no método 2, irá barrar acessos simultâneos de outras threads para o método 2 e 3.
Agora um detalhe interessante. Você poderia chamar os métodos do objeto tranca normalmente. O objeto tranca não está sendo sincronizado. Ele só está sendo usado como “marcador”.
Entendido isso, vamos para o que o java faz. Quando você escreve “synchronized”, sem especificar a tranca, quem é trancado é o this. Isso não significa que os métodos do objeto serão todos automaticamente bloqueados. Como eu falei acima, o objeto da tranca só é usado como sinalizador. O que é bloqueado são só os trechos sincronizados com aquele sinalizador.
Portanto, vamos ao que seu código faz:
public class MinhaClasse
{
void metodo1()
{
metodo2();
synchronized(this)
{
//declarações
}
}
public void metodo2() {
//faz qualquer coisa
}
}
Vamos supor que uma thread entre no seu objeto, e comece a executar o método 1. Ela passa pelo método 2, volta para o método 1 e, em seguida, obtem a tranca (this) e, enquanto está no trecho sincronizado, a preempção ocorre.
Uma segunda thread entra em ação e chama o metodo1(). Como não precisa de tranca até aqui, ela chama o método2. O método2 também não exige tranca, então ela executa.
Aí ela chega no trecho “sinchronized(this)”. Ela olha o “this” e pergunta. Está livre ou ocupado? E percebe que está ocupado, pois outra thread está no bloco. Então, cruza as pernas, torce para não urinar nas calças, e espera a outra thread sair do banheiro.
O processador, que não é burro, percebe que essa thread agora está esperando, e troca de thread. A primeira thread então termina de executar o bloco sincronizado e libera a tranca.
O processador cedo ou tarde volta para a thread 2, que verifica novamente como está a situação da porta do banheiro. “this” agora está livre. Ele entra no trecho sincronizado, e executa.
Um detalhe. Vamos supor que, enquanto a thread 2 esteja no trecho sincronizado, uma thread 3 entre em ação e chame o método 2. Esse método executa normalmente, já que não exige tranca. Não interessa se o próprio objeto está marcado como ocupado. Se não há bloco sincronized, então, ali não se trata do banheiro (você pode andar livremente pelo resto do avião, mesmo o banheiro estando ocupado).
Por isso comentei que quem é bloquado não é o “this”, mas as threads que tentarem entrar em trechos sincronizados com o this.