2010-06-29 3 views
5
public class Main2 { 
    public static void main(String[] args) { 
     new Test2().start(); 
     new Test2().start(); 
    } 
} 

class Test2 extends Thread { 
    @Override 
    synchronized public void run() { 
     try { 
      System.out.println("begin wait"); 
      wait(); 
     } catch (Exception ex) { 
     } 
    } 
} 

Comme le résultat réel de l'exécution du test: commencer attente, commencent attente, deux fois des deux fils. Contrairement au résultat attendu: commencer attendre, une seule fois de l'un des deux threads car wait() est appelée dans la méthode run() synchronisée. Pourquoi appeler la synchronisation de thread wait() de Object?Appel à l'attente() Java Object casse la synchronisation des threads

Ça fait beaucoup!


public class Main3 { 

    public static void main(String[] args) { 
     Test3 t = new Test3(); 
     new Thread(t).start(); 
     new Thread(t).start(); 
    } 
} 

class Test3 implements Runnable { 
    synchronized public void run() { 
     try { 
      System.out.println("begin wait"); 
      wait(); 
     } catch (Exception ex) { 
     } 
    } 
} 

@akf & @Sean Owen

Merci pour vos réponses. Désolé pour mon erreur, maintenant j'ai modifié le code pour placer la synchronisation sur le même objet(), le résultat est resté: commencer attendre, commencer attendre, deux fois.

@akf

attente libérera le verrou qui Synchronize a saisi, et sera re-une fois obtenu le fil est informé.

Pourriez-vous élaborer un peu?

Répondre

2

Vous avez deux objets Test2 différents. Les méthodes synchronisées se verrouillent sur l'objet. Ils n'acquièrent pas le même verrou, donc non, il devrait imprimer deux fois.

10
  1. L'objet que vous synchronisez dans cet exemple n'est pas la classe, mais l'instance, de sorte que chaque nouvel objet Test2 serait synchronisent sur un autre moniteur. La méthode que vous cherchez peut-être ici est sleep, pas wait. wait va libérer le verrou que synchronized a saisi, et sera récupéré une fois le thread notifié.

Notez que pour que votre test fonctionne correctement, vous devez verrouiller un objet commun. Si vous voulez voir wait en action, j'ai jeté ensemble une application simple qui fera apparaître un cadre avec un bouton "Notifier". Deux threads démarrent qui attendent un objet commun et sont à leur tour notifiés lorsque le bouton est enfoncé.

public static void main(String[] args) 
{ 
    final Object lock = new Object(); 

    final JFrame frame = new JFrame("Notify Test"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    JButton button = new JButton("Notify"); 
    button.addActionListener(new ActionListener(){ 
     public void actionPerformed(ActionEvent evt) { 
      synchronized(lock) { 
       lock.notify(); 
      } 
     } 
    }); 
    frame.add(button); 

    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      frame.setVisible(true); 
     } 
    }); 

    new Thread(new Runnable() { 
     public void run() { 
      synchronized(lock) { 
       try { 
        System.out.println("1. starting"); 
        lock.wait(); 
        System.out.println("1. step 1"); 
        lock.wait(); 
        System.out.println("1. step 2"); 
       } catch (InterruptedException ie) { 
        ie.printStackTrace(); 
       } 
      } 
     } 
    }).start(); 
    new Thread(new Runnable() { 
     public void run() { 
      synchronized(lock) { 
       try { 
        System.out.println("2. starting"); 
        lock.wait(); 
        System.out.println("2. step 1"); 
        lock.wait(); 
        System.out.println("2. step 2"); 
       } catch (InterruptedException ie) { 
        ie.printStackTrace(); 
       } 
      } 
     } 
    }).start(); 

} 

Pour une explication simple de wait, le JavaDoc est toujours un bon endroit pour commencer:

Causes du thread en cours d'attendre un autre thread appelle la méthode notify() ou notifyAll() méthode pour cet objet. En d'autres termes, cette méthode se comporte exactement comme si elle effectuait simplement l'appel wait (0).

L'unité d'exécution actuelle doit posséder le moniteur de cet objet. Le thread libère la propriété de ce moniteur et attend jusqu'à ce qu'un autre thread notifie les threads en attente sur le moniteur de cet objet de se réveiller via un appel à la méthode notify ou à la méthode notifyAll. Le thread attend ensuite jusqu'à ce qu'il puisse récupérer la propriété du moniteur et recommence l'exécution.

+0

Merci pour l'exemple et la référence javadoc! – sof

+1

Il peut être bon de noter que le choix du thread à réveiller est arbitraire. Une bonne implémentation sera juste et notifiera les threads dans l'ordre appelé wait(), mais ce n'est pas obligatoire. La seule contrainte est donc que [N]. l'étape 1' arrive avant '[N]. étape 2', où N est toujours 1 ou 2. –

+0

@ Mark Peters, c'est un bon point à faire - même si cela peut ne pas sembler arbitraire (par exemple, des preuves empiriques pourraient essayer de vous convaincre qu'il est ordonné), n'est aucune garantie. – akf

1

un exemple simple qui peut vous aider est la suivante:

 public class test { 
      public static void main(String[] args) { 
       Prova a=new Prova(); 
       new Test2(a).start(); 
       new Test2(a).start(); 
      } 
     } 
     class Prova{ 
      private boolean condition; 

      public void f(){ 

       while(condition){ 
        //Thread.currentThread Returns a reference to the currently executing thread object. 
        //Thread.getName() return name Thread 
        System.out.println(Thread.currentThread().getName()+" begin wait"); 
        try{ 
         wait(); 
        }catch(InterruptedException c){return;} 
       }  

       System.out.println(Thread.currentThread().getName()+" first to take the mutex"); 
       condition=true; 

      } 
     } 
     class Test2 extends Thread { 
      private Prova a; 
      private static boolean condition; 


      public Test2(Prova a){ 
       this.a=a; 
      } 
      @Override 

      public void run() { 
       synchronized(a){ 
       try {   
        a.f();   
       } catch (Exception ex) { 
       } 
       } 
      } 
     } 

dans ce cas, les deux fils synchronisent un objet, la première prenant le message de libération de verrouillage, le second attend. dans cet exemple utilise la variable d'état

-1

résumé attendre/notify mécanisme:

1) fil courant atteint une bloc de l'objet de code synchronisé qui contient l'appel à attendre(), il est en concurrence avec d'autres fils pour la lock (le moniteur de l'objet), en tant que gagnant, il exécute le bloc jusqu'à ce que l'appel à wait() rencontre. 2) en appelant wait(), le thread actuel libère le verrou sur d'autres threads concurrents, puis arrête l'exécution, attend l'envoi de la notification d'un autre thread qui parvient à obtenir le verrou.

JavaDoc:

Un fil devient propriétaire de le moniteur de l'objet dans l'une des trois façons :

• En exécutant une méthode par exemple synchronisé de cet objet. • En exécutant le corps d'une instruction synchronisée qui se synchronise sur l'objet.

• Pour objets de type Class, en exécutant une méthode statique synchronisée de la classe .

3) un autre fil atteint le même objet bloc d'un autre code synchronisé qui contient l'appel à notifier/notifyAll(), est en concurrence avec d'autres fils de la serrure, en tant que gagnant, il exécute le bloc jusqu'à ce que la finition de la appel à notifier/notifyAll(). Il va libérer le verrou soit par appel à wait() ou à la fin de l'exécution sur le bloc. 4) lors de la réception de notify/notifyAll(), le thread courant entre en compétition pour le verrou, en tant que gagnant, l'exécution continue là où elle s'est arrêtée.

exemple simple:

public class Main3 { 

    public static void main(String[] args) { 
     Test3 t = new Test3(); 
     new Thread(t).start(); 
     new Thread(t).start(); 
     try { 
      Thread.sleep(1000); 
     } catch (Exception ex) { 
     } 
     t.testNotifyAll(); 
    } 
} 

class Test3 implements Runnable { 

    synchronized public void run() { 

     System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock"); 
     try { 
      wait(); 
     } catch (Exception ex) { 
     } 
     System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock again"); 
     try { 
      Thread.sleep(1000); 
     } catch (Exception ex) { 
     } 
     System.out.println(Thread.currentThread().getName() + ": " + "bye wait block"); 

    } 

    synchronized void testNotifyAll() { 
     System.out.println(Thread.currentThread().getName() + ": " + "notify block got the lock"); 
     notifyAll(); 
     System.out.println(Thread.currentThread().getName() + ": " + "notify sent"); 
     try { 
      Thread.sleep(2000); 
     } catch (Exception ex) { 
     } 
     System.out.println(Thread.currentThread().getName() + ": " + "bye notify block"); 
    } 
} 

sortie:

Discussion-0 (ou 1): attendre le bloc a obtenu les verrouillage

Discussion-1 (ou 0): wait bloc obtenu la serrure

principal: notifier bloc a obtenu le serrure

principal: notify envoyé

principal: bye informer bloc

Discussion-0 (ou 1): attendez bloc obtenu à nouveau le verrou

Discussion-0 (ou 1): bye attendre bloc

discussion-1 (ou 0): wAIT bloc obtenu à nouveau le verrou

discussion-1 (ou 0): par e wait block

Questions connexes