Thread

Por gentileza alguem poderia me explicar pq o resultado desse codigo é sempre ou wallace wallace,ou goiac goic ou 1-1 ou 2-2
ou seja segue sempre uma repeticao?

  public  class NewClass implements Runnable{
  private String name;
     public NewClass(String n){
         name=n;
     }
         public synchronized  static void main(String[] args){
      new Thread(new NewClass("wallace")).start();
      new Thread(new NewClass("gloiac")).start(); 
  }

    public  void run() {
        me(1);
        me(2);
    }
      private void me(int a){
          System.out.println(" "+name +" - "+a);
      }
 }

Dentro do método run() você chama duas vezes o método me(). Por isso o mesmo nome é exibido duas vezes…

No entanto, não é exibido 1-1 ou 2-2, veja abaixo como ficou aqui:

Foram disparadas duas threads sequencialmente, mas não há garantias de que o resultado seja sempre o mesmo.

O programa não controla a ordem de execução das threads. Quem faz isso é a JVM, que irá escalonar o processador para atender às duas threads simulando um processamento paralelo.

Threads recém criadas têm a mesma prioridade e a JVM escolhe aleatoriamente aquela que será executada.

Para ver como funciona o escalonamento faça testes alterando a prioridade das suas threads.

[quote=thundercas]Dentro do método run() você chama duas vezes o método me(). Por isso o mesmo nome é exibido duas vezes…

No entanto, não é exibido 1-1 ou 2-2, veja abaixo como ficou aqui:

[quote]
wallace - 1
wallace - 2
gloiac - 1
gloiac - 2
[/quote][/quote]

beleza,eu queria saber onde entra o synchronized ?pois se o retirarmos do codigo o resultado defere,estou estudando esta parte mas
nao estou entendendo .O bloqueio de um objeto.No livro explica que este é um exemplo pois
o modificador é synchronized e somente um theads pode acessar um objeto de cada vez,mas
na pratica nao intendi a logica :cry:

Quando se usa a palavra synchronized em um método, toda Thread que acessar esse método irá ANTES obter um lock (trava) do objeto para só então executar o método. Quando a Thread termina a execução do método ela libera o lock pra que outra Thread possa pegar o lock também e executar o método.

E tudo isso (obtenção do lock, execução, liberação do lock) é feito automaticamente quando a Thread chama o método synchronized. E duas threads não podem obter o lock ao mesmo tempo. Isso serve pra controle de concorrência, isto é, impedir que duas Threads chamem o método ao mesmo tempo e gerem dados inconsistentes no objeto. Em resumo, o synchronized faz com que o acesso seja serializado, isto é, uma thread de cada vez chama os métodos synchronized daquele objeto. Tem outros detalhes, mas acho que vc entendeu, certo?

Sim, mas o synchronized não tem nada a ver com esse caso.

O que acontece é o que já falaram, você chama duas vezes o me().

Não há garantias de que você vá obter a mesma palavra duas vezes seguidas. As chances disso ocorrer são grandes, mas não são 100%. Pode ocorrer de, entre uma chamada a me e outra, o SO trocar as duas threads e você ter as duas palavras alternadas como saída. Isso é ainda mais provável num processador dual core.

Agora, embora isso seja possível, é improvável. Por isso talvez você não tenha conseguido obter o resultado que você esperava, o de algumas vezes o programa alternar a impressão dos nomes.

O synchronized faz o bloqueio de uma thread, que está percorrendo um objeto. Para que ele tenha efeito, duas threads tem que tentar entrar no bloco sincronizado do mesmo objeto.

Nenhuma das threads criadas por você percorre o método main. Elas iniciam no run(), imprimem duas vezes e morrem. Por isso, seu synchronized não está servindo para absolutamente nada. Elas também não entram em nenhum bloco sincronizado, já que nem o método run() e nem o me() são sincronizados.

Aqui vai um exemplo do bloco sincronized. Primeiro, vamos declarar uma classe que faz a impressão de qualquer coisa 20 vezes.

public class Printer { public void print(String word) { for (int i = 0; i < 20; i++) { System.out.println(word + "-" + i); } } }

Agora, vamos criar 2 classes, que usam essa classe para imprimir "Vinicius" e "Fabio", que usa a impressora 20 vezes (e, portanto, imprime o nome 400x).

[code]public class NameWriter implements Runnable {
private String name;
private Printer printer;

public NameWriter(String name, Printer printer) {
this.name = name;
}

public void run() {
for (int i = 0; i < 20; i++) {
printer.print(name);
}
}
}
[/code]

Ok, agora dispare esse código com o seguinte main:

[code]public class Teste {
public static void main(String args[]) {
Printer p1 = new Printer();
//Criamos dois NameWriters usando A MESMA impressora
NameWriter nw1 = new NameWriter(“Vinicius”, p1);
NameWriter nw2 = new NameWriter(“Fabio”, p1);

  //Disparamos o método run dos namewriters em 2 threads
  new Thread(nw1).start();
  new Thread(nw2).start();

}
}[/code]

O que parece que vai acontecer? Que os nomes serão impressos de 20 em 20, afinal existe uma única impressora e seu método imprime o nome 20 vezes de uma só vez. Assim, dois nomes não vão se intercalar, certo?

Entretanto, ao rodar o programa não é isso que acontece… alguns nomes aparecem intercalados e as vezes a impressora "se perde". Por que isso ocorre?

Porque ambas as threads entram no método print ao mesmo tempo.

Note que eu não chamei o NameWriter de thread, porque ele não é uma thread. Duas threads foram criadas percorrendo objetos do tipo Namewriter e, quando as linhas de execução chamaram o método print, elas se deslocaram para outro objeto. Isso é um conceito muito importante! Threads são as linhas de execução criadas após o start(), não os objetos.

Como evitaríamos isso? O fato é que duas threads não podem "cohabitar" o método print. O método print deveria imprimir tudo de uma thread, e depois tudo de outra. Para fazer essa exclusão mútua, usamos a palavra chave synchronized.

Quando uma thread entra num bloco synchronized, ela tenta obter o objeto ao qual estamos aplicando a sincronização. Esse objeto é chamado de monitor. Se esse objeto estiver disponível, ela segura o monitor e entra no bloco. Quando sair do bloco, ela libera o monitor. Quando uma thread tenta entrar no bloco, mas não consegue obter o monitor, ela simplesmente aguarda até que aquele objeto esteja disponível.

E que objeto é esse? Num bloco synchronized, o objeto é o this. No caso de criarmos esse bloco na impressora, o objeto seria a própria impressora p1. Ou seja, fazer na impressora:

public synchronized void print(String word) { for (int i = 0; i < 20; i++) { System.out.println(word + "-" + i); }

É o mesmo que fazer:

   public void print(String word) {
      synchronized (this) {
         for (int i = 0; i < 20; i++) {
            System.out.println(word + "-" + i);
         }
      }

Experimente rodar o código com o synchronized. Você vai ver que o problema não ocorre mais. Os nomes sempre são impressos de 20 em 20.

Isso tem uma importante consequência. Duas threads só serão sincronizadas se o objeto this do bloco synchronized for o mesmo. Experimente manter o bloco synchronized e criar duas impressoras ao invés de uma. Depois, passe uma impressora para cada NameWriter. Você vai notar que o problema "volta a ocorrer". Na verdade, o que acontece é que threads diferentes estão percorrendo impressoras diferentes. Portanto, o this de cada impressora refere-se a uma impressora diferente e não há sincronização.

Isso porque, na verdade, o problema não voltou a ocorrer, você vai notar que os fors nunca se perdem e, apesar de os nomes se intercalarem, os nomes serão impressos 400 vezes. O que você tem é uma execução realmente em paralelo, em duas impressoras diferentes.

Uma das formas de voltar a sincronizar seria fazer as duas impressoras compartilharem um objeto, e fazer o synchronized sobre esse objeto. O objeto monitor pode ser um objeto qualquer. Deixo como exercício para você fazer essa alteração.

Qualquer dúvida é só perguntar.

depois dessa aula do vinny nem vou postar minha sugestao hehe show de bola… esse cara é fera demais! :smiley: parabens vinny!!

Bom Vinny muito obrigado pelo esclarecimento!!Esta parte de threads estou vendo agora e to tendo dificuldades mas agora melhorou bastante !!
valeu mesmo!!

Ola Vinny,so para ver se entendi mesmo ou fiz confusao:
Para garantir o bloqueio de um objeto logo uso synchronized assim o metodo ele será acessado por um threads de cada vez
fiz um exemplo aqui mais ou menos como tu falaste:
eu notei que para ter certeza do bloqueio entao para que emprima Hokuto,Hokuto…20 vezes e depois Noken,Noken…20 vezes tenho que synchronizar o metodo run.
se fizer porem synchronizar o metodo impr() nao vai dar sempre certo, as vezes ele escalona os threads,e isso mesmo? Achavo que seria equivalente como tambem
synchronizar uma parte de codigo como no teu exemplo synchronized(this)
a ultima duvida é?pq se colocar um metodo static sentro de impr() o bloqueio nao é mais garantido?

  public  class NewClass extends Thread {
   String n;
   int x;
   NewClass(String g,int y){
      n=g;
      x=y;
   }
    @Override
      public synchronized   void run(){
         impr();
         
    
         
      }
      public   void impr(){
         
          for(int i=0;i < 20; i++)
            System.out.println("  " + n + x+Thread.currentThread().getName());
         
              
      }
 
 public  static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new NewClass("Hokuto",1)); 
        t.setName("thread fabio");
        t.start();
    
         Thread t2=new Thread(new NewClass("NoKen",2));
        t2.setName("thread ariel");
         t2.start();
           
         }

  
 }   

a ultima duvida é?pq se colocar um metodo static sentro de impr() o bloqueio nao é mais garantido?

import java.io.*;
import java.lang.*;
import java.util.*;


  public  class NewClass extends Thread {
   String n;
   int x;
   NewClass(String g,int y){
      n=g;
      x=y;
   }
    @Override
      public synchronized  void run(){
         impr();
         x();
    
         
      }
    static void x(){
         System.out.println("XXXXXXXXXXXXXXXXXXXXXX");
    }
      public  void impr(){
         
          for(int i=0;i < 20; i++)
            System.out.println("  " + n + x+Thread.currentThread().getName() + i);
         
              
      }
 
 public  static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new NewClass("fabio",1)); 
        t.setName("thread fabio");
        t.start();
    
         Thread t2=new Thread(new NewClass("Ariel",2));
        t2.setName("thread ariel");
         t2.start();
           
         }

  
 }

valeu!!

Só tem umas pequenas imprecisões na explicação do Vini. Vejamos:

uma thread não tenta obter o objeto, Vini, ela tenta obter o lock do objeto. E monitor é o nome da técnica, criado por Hoare na década de 70, que permite implementar esse tipo de exclusão mútua. Então ele não segura o monitor, mas sim o lock associado ao objeto.

[quote=jdefarge]Só tem umas pequenas imprecisões na explicação do Vini. Vejamos:

uma thread não tenta obter o objeto, Vini, ela tenta obter o lock do objeto. E monitor é o nome da técnica, criado por Hoare na década de 70, que permite implementar esse tipo de exclusão mútua. Então ele não segura o monitor, mas sim o lock associado ao objeto. [/quote]

Eu sei que a explicação não era tão precisa. É aquele negócio, abri um pouco mão da absoluta precisão técnica em prol da didática.

Eu já tentei explicar locks de objeto aqui e vi que não tive muito sucesso. Entrar em detalhes da terminologia do monitor não ajuda muito também. Melhor deixar o pessoal entender a mecânica, para depois de amadurecidos no conceito entender detalhes como esse. :slight_smile:

Não… cada thread inicia num objeto com seu próprio run(). Se você tem 2 objetos, em 2 threads diferentes, não há sincronização. Há paralelismo.

Não. Enquanto uma thread estiver no método impr(), a outra vai ter que esperar. Se ela for escalonada, ela tentará obter o lock do monitor e irá voltar a dormir. A thread que está dentro do impr() cedo ou tarde acorda e não libera o lock enquanto não terminar o processamento.

O bloqueio continua garantido, mas não mais para um objeto da impressora, mas a exclusão ocorrerá em todas as classes da impressora. Fazer o bloqueio num método synchronized é mais ou menos o mesmo que fazer:

synchronized (NewClass.class) { // Alguma coisa }