如何避免Java中的忙等待问题

7

我有一个多线程应用程序,在其中一个线程向另一个线程发送消息。等待的线程轮询消息并做出反应(锁已处理)。代码如下:

等待的线程代码:

while(true)
{
  if(helloArrived())
    System.out.println("Got hello");
  if(byeArrived())
    System.out.println("Got bye");
  if(stopArrived())
    break;
}

我希望避免使用这种占用CPU的技术,而使用其他替代方案。有什么想法吗?
编辑:实际代码如下:
BlockingQueue<Mail> killMeMailbox = new LinkedBlockingQueue<Mail>();
BlockingQueue<Mail> messageMailbox = new LinkedBlockingQueue<Mail>();

public void run()
    {
        while(true)
        {
            if(killMeMailbox.size() > 0)
            {
                break;
            }
            if(messageMailbox.size() > 0)
            {
              System.out.println(messageMailbox.poll());
            }
        }
     }

public void receiveMail(Mail mail)
    {
        //kill
        if(mail.from == -1)
        {
            killMeMailbox.add(0);
        }
        else
        {
            //other
            try
            {
                messageMailbox.put(mail);
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
            }
        }
    }

http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html - Victor Sorokin
1
你需要一个消息队列。你可以使用BlockingQueue,这样等待的线程将被阻塞,直到消息到达。 - Leonidos
我实际上正在使用这个数据结构。但在函数中我会检查大小并在处理时将其清除。这样做有错吗? - Josh
4个回答

6

避免这种情况的正确方法是使用java.lang.Object实现的等待/通知机制,或者使用Java类库提供的更高级别的并发机制:

(选择最适合您特定用例的机制...)


使用Thread.sleep不是一个好的解决方案。虽然它可以减少CPU负载(相对于轮询循环),但其缺点是会降低响应速度。
我现在正在使用BlockingQueue。但也许我使用的不正确。我刚刚添加了实际的代码。你看到我的问题了吗?
是的。您正在以一种旨在避免阻塞的方式使用队列。这是错误的方法。您应该使用take()(它将阻塞,直到条目变为可用)而不是poll(),并且删除测试队列大小的代码。
您的“killMeMailbox”东西似乎旨在允许您停止等待邮件。您应该能够使用Thread.interrupt来实现它。 (中断将取消阻止take()调用...)

我建议提到LockSupport而不是wait/notify。它是wait/notify的一对一替代品,但不会遭受竞态条件的影响。 - Marko Topolnik
我现在正在使用一个BlockingQueue。但是也许我使用不正确。我刚刚添加了实际的代码。你看到我的问题了吗? - Josh
@MarkoTopolnik - 我听到你说的话,但我不熟悉它。我可以建议您添加自己的答案来解释如何使用它。 - Stephen C
太好了!Take() 是我所缺少的。谢谢你! - Josh
这些机制本来就太底层了,所以在这里写整个答案也没有意义。但是由于 wait/notify 实际上已被弃用(应该说是不建议使用),我建议不要提及它。 - Marko Topolnik

2
你正在做忙等待。你不应该这样做,因为它浪费了CPU周期。一个线程或进程等待某个事件应该处于阻塞状态。以下是可能实现这一点的方法:

0

-1

3
在某些情况下使用 sleep 可能起作用,但出于响应性的原因,使用 notifyAll()/wait() 更好。 - Victor Sorokin
然后只需将一小段时间置于休眠状态或使用另一个数据结构。我认为有一些像BlockingQueue这样的结构允许线程等待,直到队列中有东西。 - vicsana1

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