Как избежать занятого вращения в Java

У меня есть многопоточное приложение, в котором поток отправляет сообщение другому потоку. Ожидающий поток опрашивает сообщение и реагирует (блокировки обрабатываются). Как это:

Код ожидающего потока:

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

Я хочу избежать этой техники перегрузки процессора и вместо этого использовать что-то другое. Любые идеи?

Изменить: фактический код ниже:

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());
            }
        }
    }

person Josh    schedule 24.10.2014    source источник


Ответы (4)


Правильный способ избежать этого — использовать механизм ожидания/уведомления, реализованный java.lang.Object, или один из механизмов параллелизма более высокого уровня, предоставляемых библиотеками классов Java:

(Выберите механизм, который лучше всего подходит для вашего конкретного варианта использования...)


Использование Thread.sleep не является хорошим решением. Хотя вы снижаете нагрузку на ЦП (по сравнению с циклом опроса), обратная сторона заключается в том, что вы снижаете время отклика.


Сейчас я использую BlockingQueue. Но, возможно, я делаю это неправильно. Я только что добавил фактический код выше. Вы видите мою проблему?

Да. Вы используете очередь так, чтобы избежать блокировки. Это неправильный подход. Вы должны использовать take() (который будет блокироваться до тех пор, пока запись не станет доступной) вместо poll() и избавиться от кода, проверяющего размер очереди.

Похоже, ваш материал "killMeMailbox" предназначен для того, чтобы вы могли перестать ждать почту. Вы должны быть в состоянии реализовать это, используя Thread.interrupt. (Прерывание разблокирует вызов take()...)

person Stephen C    schedule 24.10.2014
comment
Я бы предложил упомянуть LockSupport вместо ожидания/уведомления. Это замена 1 к 1 для ожидания/уведомления, но она не страдает от условий гонки. - person Marko Topolnik; 24.10.2014
comment
Сейчас я использую BlockingQueue. Но, возможно, я делаю это неправильно. Я только что добавил фактический код выше. Вы видите мою проблему? - person Josh; 24.10.2014
comment
@MarkoTopolnik - я слышу, что ты говоришь, но я с этим не знаком. Могу ли я предложить вам добавить свой собственный ответ, который объясняет, как его использовать. - person Stephen C; 24.10.2014
comment
Идеальный! Take() — это то, чего мне не хватало. Благодарю вас! - person Josh; 24.10.2014
comment
В любом случае, этот механизм слишком низкоуровневый, поэтому нет смысла писать здесь целый ответ только для этого. Но поскольку ожидание/уведомление фактически устарело (термин не рекомендуется), я бы воздержался от упоминания об этом. - person Marko Topolnik; 24.10.2014

Вы занимаетесь занятым ожиданием. Вы никогда не должны этого делать, потому что это тратит впустую циклы процессора. Поток или процесс, ожидающий некоторого события, должен находиться в заблокированном состоянии. Вот возможные пути достижения этого:

person ChrisJ    schedule 24.10.2014

Если вы можете переформулировать свою проблему с точки зрения исполнителя задач, рассмотрите возможность использования SingleThreadExecutor. Если вам нужно что-то более экзотическое — либо параллельная очередь или даже wait()/notify() .

person bobah    schedule 24.10.2014

Вы пытались поставить Thread.sleep, чтобы избежать постоянного выполнения цикла while? Это освободит процессор для других ваших потоков и позволит избежать зависаний.

http://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

person vicsana1    schedule 24.10.2014
comment
Хотя использование sleep может работать в некоторых случаях, использование notifyAll()/wait() лучше из соображений скорости отклика. - person Victor Sorokin; 24.10.2014
comment
Затем просто поместите небольшое время в спящий режим или используйте другую структуру данных. Я думаю, что есть некоторые структуры, такие как BlockingQueue, которые позволяют потоку ждать, пока что-то не окажется в очереди. - person vicsana1; 24.10.2014