Иметь потоки, запускаемые из слушателей событий в Java? - PullRequest
2 голосов
/ 29 мая 2011

У меня есть программа, которая создает сотни экземпляров класса, каждый из которых слушает другой поток, который просто запускает событие по регулярному расписанию (чтобы все они работали с одинаковой скоростью).Мне бы хотелось, чтобы каждый из сотен экземпляров был своим собственным потоком, чтобы при возникновении события все они могли работать параллельно.Для меня имеет смысл сделать так, чтобы эти классы расширяли класс Thread, а затем содержали этот код внутри них ...

public class IteratorStepListener implements StepEventListener { 
      public void actionPerformed(ActionEvent e) { 
           start(); 
      } 
 } 

 public void run() { 
      doStuff();
 }

Это, похоже, не работает.Понятно, что я не понимаю здесь чего-то базового.Как правильно это сделать?

Ответы [ 4 ]

4 голосов
/ 29 мая 2011

Хорошо, во-первых: избавьтесь от идеи, что ваши сотни потоков будут работать параллельно. В лучшем случае они будут работать одновременно , то есть с разделением по времени. Когда вы войдете в сотни потоков, вы увидите, что подшипники в алгоритме планирования начинают светиться; тысячами они будут курить и в конце концов схватятся, и у тебя больше не будет ниток.

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

  Thread t = new Thread(Runnable r);
  t.run();

Похоже, вы пытаетесь run() один и тот же поток снова и снова; этот путь лежит безумие. Взгляните на Wiki на Событийно-управляемое программирование . Если вы действительно хотите иметь отдельный поток для обработки каждого события, вам понадобится схема примерно такая (псевдокод):

 processEvents: function
     eventQueue: queue of Events
     event: implements Runnable

     -- something produces events and puts them on the queue

     loop -- forever
     do
         Event ev := eventQueue.front
         new Thread(ev).run();
     od
end -- processEvents             
3 голосов
/ 29 мая 2011

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

Не думаю, что это хороший подход.

  • Если у вас нет сотен процессоров, потоки не могут работать параллельно. Вы получите потоки, запускающие их по одному (по одному на процессор), или временные интервалы между процессорами.

  • Каждый поток фактически связывает значительную часть ресурсов JVM, даже когда он неактивен. IIRC, размер стека по умолчанию составляет около 1 МБ.

  • Пример кода в вашем вопросе показывает событие, вызывающее start() в потоке. К сожалению, вы можете вызвать start() в потоке только один раз. После завершения потока его нельзя перезапустить.

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

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
     keepAliveTime, timeUnit, workQueue);

...

public class IteratorStepListener implements StepEventListener, Runnable { 
    public void actionPerformed(ActionEvent e) { 
        executor.submit(this);
    }

    public void run() { 
        doStuff();
    }
} 
3 голосов
/ 29 мая 2011

Похоже, что событие будет запущено более одного раза ... но вы не можете запустить один и тот же поток более одного раза.

Похоже, ваш слушатель должен реализовать интерфейс, но запуститьпоток непосредственно в actionPerformed (или лучше, используйте Executor, чтобы он мог использовать пул потоков).Поэтому вместо текущей реализации вы можете использовать:

// Assuming the listener implements runnable; you may want to
// delegate that to a separate class for separation of concerns.
public void actionPerformed(ActionEvent e) {            
    new Thread(this).start(); 
}

или

public void actionPerformed(ActionEvent e) {            
    executor.execute(this);
}
0 голосов
/ 29 мая 2011

Вы не можете использовать такие потоки в Java. Это связано с тем, что потоки Java напрямую отображаются на базовые потоки ОС (по крайней мере, в тех реализациях JVM, о которых я знаю), а потоки ОС не могут масштабироваться таким образом. Практическое правило заключается в том, что вы хотите сохранить общее количество потоков в сотне или что-то еще в приложении. Несколько сотен, наверное, в порядке. Несколько тысяч, как правило, проблематично, в зависимости от используемого вами HW.

Использование потоков, как вы описали, является допустимой стратегией реализации на языках, таких как, например, Erlang. Между тем, если на этот раз вы застряли с Java, хорошей альтернативой может быть создание общего пула потоков и отправка в него ваших задач, а не одновременное выполнение всех задач. В этом случае вы можете выбрать подходящее количество потоков (наилучшее число зависит от характера задачи. Если вы не знаете, количество доступных ядер ЦП, умноженное на 2, - хорошее начало), и одновременно запускать это количество задач. ,

Если вам абсолютно необходимо, чтобы все задачи выполнялись одновременно, это может стать немного сложнее, но это также выполнимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...