Java中的跨线程函数调用

3

我想知道如何在Java中跨线程调用不同的函数。目前,我所做的方法是将我的线程的run()函数编写为以下方式:

public volatile boolean invokeMyFunction = false;

public void run() {
    while(true) { 
        if(invokeMyFunction) {
            MyFunction();
            invokeMyFunction = false;
        }
    }
}

如果我想从线程外运行函数MyFunction(),则写"whateverobject.invokeMyFunction = true",它将从该线程内运行我的函数,因为该循环将捕获它。这对我来说非常有效,但由于while(true)循环,它会使用100%的CPU。我可以通过在循环内加入Thread.sleep(1000)来解决这个问题,但这似乎有些混乱,而且我认为有更好的方法来解决这个问题。


有可能会发生多个函数调用吗? 你需要一个能在第二个线程上调用任意数量的函数的解决方案吗? - user406009
理想情况下,我希望有一种解决方案,可以在第二个线程上对多个函数进行多次调用。 - David Zorychta
4个回答

4
我认为在这里,最简单且对CPU友好的方法是:
public void run() {
    while(true) { 
        synchronized(foo) {
            while(!invokeMyFunction) {
                foo.wait();
            }
        }
        MyFunction();
        invokeMyFunction = false;
    }
}

上面的代码在自己的线程中运行。另一个线程可以通过以下方式让第一个线程运行MyFunction():

invokeMyFunction = true;
foo.notifyAll();

请注意: a) 你不能将invokeMyFunction设置为布尔值并在其上同步,因为在java中只有两个布尔值 :) b) 如果invokeMyFunction被设置n次,则如果在它不为false时将其设置为true,则可能仍然运行较少的次数。 c) 使用BlockingQueue可能更容易,并允许Thread 1运行任意函数:
while(true) {
    Runnable next = queue.take();
    next.run()
}

另一个线程会通过以下方式告诉它运行MyFunction:

queue.put(new Runnable() {
    void run() {
        MyFunction();
    }
});

我觉得这个方法更容易 :) 如果你想要n个线程运行队列中的任何东西,你只需要生成n个监听队列的线程即可。或者你可以学习如何使用线程池:http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html

注意:

queue.put()会阻塞直到BlockingQueue中有新的空间可用,即在“满”时会阻塞。查看您正在使用的任何BlockingQueue实现的实现文档以查看您的队列是否有限制。无论如何,请确保您添加的工作项不要超过您可以处理的时间太长。


优雅且易于实现,正是我所寻找的 - 谢谢 :) - David Zorychta

2
你可以在你的线程中放置一个监视器,并等待该监视器。当你调用函数时,告诉监视器释放一个人(应该只有一个人)来运行你的函数,然后再次返回等待状态。
另一方面,使用sleep也没有本质上的问题。我知道为什么你会认为它很混乱,但它运行出错的几率较低,并提供了服务之间更松散的耦合。
(顺便说一下 - 你不需要在线程中放置监视器。你可以将该对象作为监视器,而不是内部监视器。但这样会让其他人有可能干扰它,所以最好使用私有内部对象作为监视器。)

2

你的代码存在几个问题,除了占用CPU之外。

如果其他线程希望在 MyFunction 执行期间两次调用此线程怎么办?你可能会错过一个调用。

一种改进方法是:

public volatile boolean invokeMyFunction = false;

public void run() {
    while(true) { 
        if(invokeMyFunction) {
            // Moved here.
            invokeMyFunction = false;
            MyFunction();
        }
    }
}

然而,这只是使竞争条件的发生可能性降低了,而不是完全避免。
你最好使用像我在这里建议的BlockingQueue。让另一个线程将某些内容发布到队列中,然后由该线程读取。这样做也不会占用CPU。

1

你的方案对我来说很奇怪。为什么要启动一个线程,但却让它等待到未来某个未知的时间才被告知运行?为什么不直接在那个稍后的时间启动它呢?

编辑添加以澄清glowcoder的问题

编辑已删除-我的错,我误解了他的问题,他想能够多次调用MyFunction。在这种情况下,他应该使用其他答案中建议的某种队列。


假设另一个操作需要一定的时间才能完成,最好不要阻塞主线程。例如,考虑读取源或套接字通信。 - corsiKa
不需要阻塞,您仍然可以在另一个线程中运行其他操作。只要在准备好之前不要启动它即可。 - user949300
也许不是必须的,但每次都要启动一个新线程,或使用线程池或执行器服务。我并不完全反对学习这些有价值的东西,但它们不在问题的范围内。 - corsiKa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接