2010-06-29 76 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 等待塊