Metodos comuns e estaticos sincronizados

Boa noite pessoal.

Eu estou estudando e me apareceu uma duvida sobre métodos sincronizados.

No livro (Use a cabeça) diz que quando um segmento entra em um método sincronizado ele pega a “chave” do OBJETO pra ele e nenhum outro segmento consegue acessar esse objeto. E diz que quando um método estático sincronizado é acessado por um segmento, esse pega a “chave” da CLASSE.

Minha duvida é a seguinte:

Da pra um segmento acessar um método estático enquanto outro segmento acessa outro método nao-estico do mesmo objeto/classe??

Espero ter sido o mais claro possível.
Valeu pela ajuda.

não entendi direito mas nota que se o objeto ta sincronizado, não importa o método ou se ta estatico… o objeto só vai ser acessado por uma thread de cada vez

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:

[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.

E só pra completar o que o Vini falou, quando se usa synchronized em um método static o monitor usado é a própria classe:

class MyClass{
    static synchronized void m1(){
    }
}

equivale a :

class MyClass{
    static void m1(){
        synchronized(MyClass.class){
        }
    }
}

Opa, bom dia pessoal.
Obrigado pela atenção dada até agora.

Vamos ver se eu entendi:

Vini, continuando usando sua analogia dos banheiros, digamos que esse avião (objeto) tenha varios banheiros (metodos sincronizados). Quando alguem entrar em um desses banheiros, ninguem mais poderia usar qualquer outro banheiro desse aviao, mas as areas comum de acesso livre para todos continuariam sem ser alteradas em nada. Outras pessoas teriam acesso normal a essa areas enquanto alguem esta no banheiro.

Correto?

Outra coisa, eu nao entendi muito bem como funciona aquilo com aquele objeto tranca. Ele seria verificado se esta disponivel? Precisariam que a “chave” dele estivesse disponivel para pode entrar naquela parte sincronizada?

rmendes08, isso de a chave da propria classe que foi o que eu menos entendi. No livro diz mais ou menos assim: " se temos 3 objetos de uma classe no acervo, entao na verdade temos 4 chaves: uma para cada objeto e UMA DA CLASSE".

Se ha uma chave para a classe, como isso funciona? Apenas para métodos estáticos( que pertence a classe e nao aos objetos)? Para a classe em geral?

Desculpe se estou sendo redundante pessoal.
Valeu pela ajuda.

Isso mesmo.

Aquele objeto é o sinal de “OCUPADO” em cima do banheiro. Cada thread, ao entrar no banheiro, acende o seu sinal de ocupado. Assim, com dois objetos, você pode criar alguns métodos que só uma thread ocupe, que não interferem em outros métodos sincronizados, que outra thread pode estar ocupando.

A chave para classe é uma tranca como outra qualquer. Mas como a tranca trata-se de uma variável estática, ela será compartilhada por todos os objetos da classe.

Com chaves que não são para classe, é como se cada objeto tivesse o seu próprio sinal de ocupado. Threads diferentes poderão rodar simultaneamente em objetos diferentes.

Blz, entendi.

E quando uma Thread entrar em um método sincronizado estático, ja que esse pertence a classe, essa Thread, bloqueia todos os métodos estático sincronizados e mais todos os métodos sincronizados comum? O que acontece quando uma thread entra em um metodo sincronizado estatico?

Eu vi em um outro topico esse seu exemplo:

public class Classe {  
   public static void metodo() {  
      synchronized (Classe.class) {  
        //Faz alguma coisa  
      }  
   }
}

Nesse exemplo ela precisa da “chave” da classe completa?

Pq se la bloqueia tbm os métodos comuns (nao sei se isso acontece, essa é minha grande duvida), ela bloqueia de todos os objetos.

Voltando ao caso dos banheiros de aviao, vou fazer a pior comparação de todos os tempos, seria como se alguem interdita-se o banheiro do projeto do aviao, e com isso interdita-se todos os banheiros dos avioes ja em uso.

Acredito que essas sao todas minhas duvidas.
Espero ter conseguido explicar da melhor maneira possivel.
Obrigado.

Não, ela só bloqueia outros métodos que também estejam sincronizados com Classe.class.

No java as regras não tem exceções, nem comportamentos diferentes. Elas são sempre simples e diretas.
A única coisa que você tem que entender é que esse monitor é um objeto static e, portanto, dois objetos diferentes usarão o mesmo monitor.

Métodos não synchronized, sincronizados com outra coisa (como o this), não são afetados por uma sincronização estática.

Ah ta, agora entendi. Valeu.

Valeu Vini e a todos q ajudaram.