Synchronized(this)

9 respostas
C

Um método sincronizado bloqueia o objeto temporariamente enquanto um segmento acessa o mesmo.
Quando um bloco é sincronizado, acontece o mesmo?

public class MinhaClasse { void metodo1() { metodo2(); synchronized(this) { //declarações } } }

O objeto MinhaClasse é bloqueado quando o segmento executa as declarações?

9 Respostas

ViniGodoy
  1. O método sincronizado não bloqueia objetos, bloqueia threads;
  2. Ele só bloqueia o trecho do próprio método, não de todos os métodos do objeto.

O bloco sincronizado é similar, mas só o que tiver dentro daquele bloco será encarado como região crítica. No seu exemplo, duas threads simultaneas podem chamar metodo2().

C

Vou reestudar isso, não devo ter entendido direito.
Volto a postar depois.
Vlw! :wink:

C

To interpretando mal? Ele n fala em bloqueio por objeto?

G

Olá, agora rolou uma dúvida aqui tb.

Se o método2(); tiver uma referência a uma variavel da instância ele terá que aguardar a execução do método1();. Correto!?

Ex.:
public class MinhaClasse{  

   private String name;

   void metodo1(){  
     metodo2();  
     synchronized(this){   //declarações    }
   }
   void metodo2() {  
      name = "MyName";
   }
 //
 }
rmendes08

Não. Veja bem, blocos de código sincronizados servem para evitar que múltiplas threads executem este método ao mesmo tempo. Porém, o controle sempre é feito em cima de um objeto específico. Por exemplo, seja a classe ContaCorrente(exemplo clássico):

class ContaCorrente{
    private double saldo;
    private String nomeTitular;

    public ContaCorrente(String nomeTitular){
        this.nomeTitular = nomeTitular;
    }

    public void depositar(double valorDeposito){
        synchronized(this){
            saldo += valorDeposito;
        }
    }

    public synchronized void sacar(double valorSaque) throws SemSaldoException{
        synchronized(this){
            if(valorSaque > saldo){
                throw new SemSaldoException("Valor do saque é maior que o saldo!");
            }
            
            saldo -= valorSaque;
        }
    }
}

Nessa classe, duas (ou mais) threads não podem entrar nos blocos que alteram o valor do saldo ao mesmo tempo para a mesma conta. Por exemplo, existem 3 transações pendentes para a conta do “João da Silva” e essas 3 transações resolvem executar ao mesmo tempo. Uma delas vai obter o bloqueio do objeto ContaCorrente do “João da Silva”, de forma que as outras 2 deverão esperar a thread que obteve o bloqueio executar o bloco sincronizado. Porém, se cada uma das threads for alterar um objeto ContaCorrente diferente, por exemplo, a thread 1 altera a conta da Maria, a t2 altera a conta da Joana e a t3 altera a conta da Catifunda, não haverá espera, porque cada uma vai obter bloqueio sobre um objeto diferente.

G

Olá rmendes08, veja só, se há uma concorrência entre threads para acessar um mesmo objeto ContaCorrente e há uma chamada simultânea, a segunda ou posterior thread irá ter q aguardar até q a thread q acessou o objeto anteriormente tenha terminado de executar o bloco synchronized para que ela possa executar a linha name=“myName”.
Todos os atributos referentes àquele objeto estarão bloqueados para todas as outras threads.

ViniGodoy

Muita calma nessa hora.

Vamos primeiro identificar as coisas. Existe:

1. As threads que percorrem o código em objetos. Elas são postar para dormir (bloqueadas) e são elas que precisamos sincronizar;
2. Um objeto chamado de "monitor" que controla a sincronização;
3. 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:

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
      }
   }

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.

gRoOve

Excelente explicação ViniGodoy, tava com dúvidas em relação a este assunto, me ajudou muito :slight_smile:

ViniGodoy

Que bom. :slight_smile:

Criado 24 de novembro de 2010
Ultima resposta 19 de jul. de 2011
Respostas 9
Participantes 5