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

由于运行测试的实际结果: 从两个线程开始等待, 开始等待, 两次。 对比预期结果: 开始等待, 只有一次来自两个线程之一,因为wait()是在synchronized run()方法内部调用的。 为什么可以调用Object的wait()中断线程同步?呼叫到Java对象的wait()打破线程同步

Thans很多!


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欧文

感谢您的答复。对不起我的错误,现在我修改了代码以将同步放在同一对象的run()上,结果仍然存在:开始等待,开始等待两次。

@akf

等待将释放锁 同步已经抓住,并将于 再得到一次线程通知。

您能详细说明一下吗?

回答

2

您有两个不同的Test2对象。同步方法锁定对象。他们没有获得相同的锁,所以不应该打印两次。

10
  1. ,你是在这个例子上同步的对象不是类,但实例,因此每个新Test2对象将在不同的显示器上同步。
  2. 您可能要查找的方法是sleep,而不是waitwait将释放​​已取得的锁定,并且一旦线索通知,将会重新获取该锁定。

请注意,为了您的测试正常工作,您需要锁定一个通用对象。如果您想查看wait的实际情况,我已经将一个简单的应用程序放在一起,弹出一个带有“通知”按钮的框架。两个线程将开始等待一个公共对象,并在按钮被按下时通知。

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

} 

对于wait一个简单的解释,JavaDoc中始终是一个良好的开端:

造成当前线程等待,直到其他线程调用notify()方法或notifyAll()方法为这个对象。换句话说,这个方法的行为就好像它只是执行呼叫等待(0)一样。

当前线程必须拥有该对象的监视器。线程释放此监视器的所有权,并等待另一个线程通知对通知方法或notifyAll方法的调用,以便通知通过此对象监视器等待的线程唤醒。该线程然后等待,直到它可以重新获得监视器的所有权并恢复执行。

+0

感谢您的示例和javadoc参考! – sof 2010-06-29 14:17:48

+1

可能需要注意的是,选择哪个线程唤醒是任意的。一个很好的实现将是公平的,并会按他们所谓的wait()的顺序通知线程,但这不是必需的。所以唯一的限制就是'[N]。步骤1'在'[N]之前发生。步骤2',其中N始终为1或2. – 2010-06-29 18:11:05

+0

@马克斯彼得斯,这是一个很好的点 - 虽然它可能不是任意的(即,经验证据可能试图说服你,它是有序的),那里是没有保证的。 – akf 2010-06-29 18:17:35

1

一个简单的例子,可以帮助你的是:

 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) { 
       } 
       } 
      } 
     } 
在这种情况下,两个线程同步的对象

,第一考虑的锁释放消息,第二个等待。在这个例子使用条件变量

-1

摘要等待/通知机制:

1)当前线程到达一个对象的同步代码块包含呼叫等待(),它与其他线程的竞争锁定(对象的监视器),作为胜者,它执行该块,直到等待()的呼叫遇到。

2)通过调用wait(),当前线程释放锁到其他竞争线程,则暂停执行,等待通知正在从另一个线程谁获得锁成功发送。

的JavaDoc:

线程成为 所有者三种 途径之一对象的监视器:

•通过执行对象的同步实例 方法。

•通过执行 同步对象的同步语句 的正文。

•对于 类型为Class的对象,通过执行该 类的 同步静态方法。

3)另一个线程到达同一个对象的又一个同步代码块包含呼叫通知/ notifyAll的(),它与其他线程的锁竞争,作为赢家它执行块直到完成调用notify/notifyAll()。它将通过调用wait()或在块的执行结束时释放锁。

4)在接收到通知/ notifyAll的(),当前线程竞争锁,作为赢家的执行继续,其中它已经停止。

简单的例子:

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

输出:

线程0(或1):等待块得到了 锁定

线程1(或0):等待块得到 锁

主:通知块得到了 锁

主:NOTIFY发送

主:再见通知块

线程0(或1):等待方框 得到了锁再次

线程0(或1):再见 等待方框

线程1(或0):等待方框 得到了锁再次

线程1(或0):由e 等待块