2010-06-29 12 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) { 
     } 
    } 
} 

Como el resultado real de la ejecución de la prueba: comienzan espera, comienzan espera, dos veces a partir de los dos hilos. En contraste con el resultado esperado: begin wait, solo una vez desde uno de los dos hilos porque se llama a wait() dentro del método run() sincronizado. ¿Por qué podría llamar a la sincronización del hilo de corte wait() de Object?Llamada a la espera de Java Object() rompe la sincronización de hilos

¡Eso es mucho!


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

Gracias por sus respuestas. Perdón por mi error, ahora modifiqué el código para colocar la sincronización en la ejecución del mismo objeto(), el resultado se mantuvo: comenzar a esperar, comenzar a esperar, dos veces.

@akf

espera será liberar el bloqueo que sincronizar ha agarrado, y será re- conseguido una vez que se notificó al hilo.

¿Podría elaborar un poco?

Respuesta

2

Tiene dos objetos Test2 diferentes. Los métodos sincronizados se bloquean en el objeto. No están adquiriendo el mismo candado, por lo que no debería imprimir dos veces.

10
  1. El objeto que se está sincronizando en este ejemplo no es la clase, pero la instancia, por lo que cada nueva Test2 objeto sería de sincronización en un monitor diferente.
  2. El método que podría estar buscando aquí es sleep, no wait. wait liberará el bloqueo que synchronized ha capturado, y se volverá a obtener una vez que se notifique el hilo.

Tenga en cuenta que para que su prueba funcione correctamente, deberá bloquear un objeto común. Si quiere ver wait en acción, he creado una aplicación simple que aparecerá un cuadro con el botón "Notificar". Se iniciarán dos subprocesos que esperan en un objeto común y, a su vez, se notifican cuando se presiona el botón.

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(); 

} 

Para una sencilla explicación de wait, el JavaDoc es siempre un buen punto de partida:

Hace que el hilo actual que esperar hasta que otro hilo invoca el método notify() o la notifyAll() método para este objeto. En otras palabras, este método se comporta exactamente como si simplemente realizara la espera de llamada (0).

El subproceso actual debe ser el propietario del monitor de este objeto. El subproceso libera la propiedad de este monitor y espera hasta que otro subproceso notifique a los subprocesos que esperan en el monitor de este objeto que se activen mediante una llamada al método notify o al método notifyAll. El subproceso luego espera hasta que pueda volver a obtener la propiedad del monitor y reanuda la ejecución.

+0

¡Gracias por el ejemplo y la referencia de javadoc! – sof

+1

Podría ser bueno tener en cuenta que la elección de qué hilo para despertar es arbitraria. Una buena implementación será justa y notificará los hilos en el orden que ellos llaman wait(), pero eso no es obligatorio. Entonces, la única restricción es que '[N]. el paso 1' sucede antes de '[N]. paso 2', donde N es consistentemente 1 o 2. –

+0

@Mark Peters, ese es un buen punto para hacer, aunque podría no parecer arbitrario (es decir, la evidencia empírica podría intentar convencerlo de que está ordenado), hay no es garantía – akf

1

un ejemplo simple que puede ayudar a que es la siguiente:

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

en este caso los dos hilos sincronizar un objeto, el primero llevando el mensaje de liberación de bloqueo, el segundo uno espera. en este ejemplo se utiliza la variable de condición

-1

resumen que esperar/notificar mecanismo:

1) hilo actual alcanza bloque de código sincronizado de un objeto que contiene la llamada a wait(), compite con otros hilos para la lock (el monitor del objeto), como ganador ejecuta el bloque hasta que se encuentra la llamada a wait().

2) al llamar a wait(), el hilo actual libera el bloqueo a otros hilos competidores, luego detiene la ejecución, espera que se envíe una notificación desde otro hilo que logre obtener el bloqueo.

JavaDoc:

Un hilo se convierte en el dueño de el monitor del objeto en una de tres maneras :

• Mediante la ejecución de un método de instancia sincronizado de ese objeto.

• Al ejecutar el cuerpo de una instrucción sincronizada que se sincroniza en el objeto.

• Para objetos de tipo Clase, ejecutando un método estático sincronizado de esa clase .

3) otro hilo alcanza todavía otro bloque de código sincronizado del mismo objeto que contiene la llamada para notificar a/notifyAll(), que compite con otros hilos para la cerradura, como ganador se ejecuta el bloque hasta terminar la llamada para notificar/notificar a All(). Liberará el bloqueo mediante llamada a wait() o al final de la ejecución en el bloque.

4) al recibir notify/notifyAll(), el hilo actual compite por el bloqueo, como ganador la ejecución continúa donde se ha detenido.

ejemplo sencillo:

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"); 
    } 
} 

de salida:

Tema-0 (o 1): esperar bloque obtuve bloquear el

Tema-1 (o 0): bloque de espera tiene la cerradura

principal: notificar bloqueo obtuvo la cerradura

principal: Notificar enviado

principal: bye notificar bloque

Tema-0 (o 1): esperar bloque tiene la cerradura de nuevo

Tema-0 (o 1): bye espere bloquear

Tema-1 (o 0): esperar bloque tiene la cerradura de nuevo

Tema-1 (o 0): por e bloque de espera

Cuestiones relacionadas