2010-09-28 195 views
1

我有點問題。我有一個Java應用程序,當它啓動時,它需要製作六個或許七個線程,等待發生某種事件(即用戶按下一個按鈕)。下面是我如何製作這些線程的一個例子。問題是,我打開我的任務管理器只是爲了看到我所有的四個cpu核心都處於100%(大約從20開始),只要我關閉我的應用程序,一切都恢復正常。我對多線程仍然很陌生,我知道我在這裏犯了一些併發罪,但是我希望對如何糾正這種情況有所瞭解。讓線程等待

new Thread (new Runnable() { public void run() 
{ 
    while (true) 
     if (doSomeFunction) 
     { 
      myFunction(); 
      doSomeFunction = false; 
     } 
} }).start(); 

// Somewhere else in the code 
doSomeFunction = true; 

我想或許等待,並通知將是正確的做法呢?

編輯:只是爲了澄清,這無關與Swing組件供電。相反,這是基於腳本的事件,我不希望某些腳本函數阻塞,而是在完成後臺任務時立即返回。

+0

你的任務是幹什麼的?如果我是誠實的,產生線程根據一些UI觸發事件簡單地使它們活躍起來並不會有多大意義。一旦用戶點擊了一個按鈕,你能不能產生線程來完成工作? – dannywartnaby 2010-09-28 16:38:28

+0

多數民衆贊成我最初是這樣的,但我得到的理解,使我需要的線程和重用他們會更好。 – Dave 2010-09-28 16:50:18

回答

5

這裏有兩個可能的答案。

第一個是你正在等待按下按鈕的Swing應用程序(或類似)。如果是這樣,線程不是答案。你應該簡單地使用動作監聽器:

JButton button = ... 
button.addActionListener(new ActionListener() { 
    void actionPerformed(ActionEvent e) { 
    // do stuff 
    } 
}); 

在Swing應用程序中使用線程需要非常小心。這是因爲只有一個線程(稱爲事件分派線程或EDT)是允許進行UI更新的唯一線程。所有事件處理程序都發生在EDT中。因此,當代碼在EDT上運行時,不會發生UI更新(移動/關閉窗口,按下按鈕等),所以EDT代碼應該是短暫的。

更一般的問題可以在一些依賴於比提供更多信息的方式來解決。

首先,要回答你的問題:你的CPU使用率會爲100%,因爲線程不斷檢查值,並呼籲在一個無限循環的功能。

你應該然而使用了Java 5+併發模型,這看起來會更像是這樣的:

ExecutorService threadPool = Executors.newFixedThreadPool(10); // or whatever 
threadPool.submit(new Runnable() { 
    public void run() { 
    // do stuff 
    } 
}); 

至於如何做線程間通信,這就是它得到棘手。你共享一個原始變量,這是一個非常糟糕的主意。

最簡單的成語是使用等待通知:

final Object someObject = ... 
ExecutorService threadPool = Executors.newFixedThreadPool(10); // or whatever 
threadPool.submit(new Runnable() { 
    public void run() { 
    synchronized (someObject) { 
     someObject.wait(); 
    } 
    // do stuff 
    } 
}); 
... 
synchronized (someObject) { 
    someObject.notify(); // wakes ONE waiting thread 
} 

的​​互斥量真的重要。

但是Java 5增加了一大堆替代品。我會建議購買Java Concurrency in Practice並閱讀封面封面。

+0

+1:使用Swing值得一提的是,有一個專用線程將事件分派給ActionListener(和其他回調類),這就是爲什麼不需要創建自己的事件。 – Adamski 2010-09-28 16:41:22

+0

這看起來像一個很好的模型,但我有幾個問題。 someObject究竟是什麼,以及如果我想多次運行它,會發生什麼情況,就像我的例子中我想在任何時候運行myFunction()函數一樣。 – Dave 2010-09-28 16:57:18

+0

@Dave'someObject'是任何對象。從技術上講,它被稱爲*監視器*。您可以使用自定義對象在線程之間傳遞數據,或者如果沒有必要,可以使用:Object someObject = new Object();'。如果您想多次執行,您也可以在'while(true){...}'循環或類似內容中嵌入'run()'方法的主體。 – cletus 2010-09-28 17:26:08

1

在while循環結束時,你應該有類似Thread.sleep(250)。這會導致當前線程休眠250毫秒,這樣CPU就不會燒死循環。

3

你在做什麼叫做Busy waiting。 快速修復的問題是將Thread.sleep(1)添加到代碼中。這應該已經足以使CPU利用率降低。

然而,正確的解決方案是使用適當的同步技術,例如,一個Semaphore

4

如果你想控制,當你所有的線程真正開始自己的工作,在CountDownLatch介紹INT Java 5的可能是一個簡單的解決方案:

public void static main(String[] args) { 
    final CountDownLatch signalToDoSomeFunction = new CountDownLatch(1); 

    new Thread(new Runnable() { 
    public void run() { 
     // wait until the latch is "counted down" 
     signalToDoSomeFunction.await(); 
     myFunction(); 
    }); 

    // create other threads similarly 

    // all thread created but waiting, now ready to start work 
    signalToDoSomeFunction.countDown(); 

} 

這消除了inifinte循環。 CountDownLatch將Object.wait()和Object.notify()調用隱藏在更好的接口後面。