2010-09-28 161 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()调用隐藏在更好的接口后面。