Пользовательское событие Java в потоке отправки событий

Я создаю приложение Java, и я реализовал пользовательские прослушиватели и события. Теперь проблема заключается в том, что существует много событий и прослушивателей, а поток графического интерфейса приложения блокируется, пока прослушиватели обрабатывают события. Можно ли решить эту проблему с помощью потока диспетчеризации событий? Если да, то как?

Код для слушателей:

public class ProjectChangeObserver
{
    private List<ProjectChangeListener> listeners = new ArrayList<ProjectChangeListener>();

    public void addProjectChangeListener(ProjectChangeListener l)
    {
        listeners.add(l);
    }

    public void removeProjectChangeListener(ProjectChangeListener l)
    {
        listeners.remove(l);
    }

    public void removeAllProjectChangeListeners()
    {
       listeners.clear();
    }

    public void fireProjectChange(ProjectChangeEvent e)
    {
      if(listeners.size() > 0)
       { 
          ArrayList<ProjectChangeListener> safeCopy = new ArrayList<ProjectChangeListener>(listeners);
          for (ProjectChangeListener cl : safeCopy ) {
              cl.onProjectChange(e);
        }
       } 
     }
}

person blejzz    schedule 30.07.2012    source источник


Ответы (3)


Одной из особенностей Swing является то, что он является однопоточным.

Однако ничто не мешает вам создавать собственные потоки для фоновой обработки. Пока вы делаете вызовы только revalidate() или repaint(), весь рисунок будет по-прежнему обрабатываться в потоке Swing.

(Помните, что если вы собираетесь порождать другие потоки для выполнения фоновой работы, убедитесь, что ваши переменные объявлены volatile, если вы ожидаете, что поток Swing увидит те же значения, что и в ваших потоках. Или передайте информацию обратно через другого слушателя.)

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

Если вы обнаружите, что ваши Listener сильно нагружают ЦП/сеть, вы можете подумать об архитектуре того, кто больше всех кричит, а кто слушает. Я всегда стараюсь свести к минимуму количество криков в своем коде «представления», чтобы он просто реагировал на события.

Между прочим, я нашел Project Lombok и его бета-сестра Lombok PG, чтобы значительно упростить шаблон реализации шаблона Listener с помощью @ListenerSupport.

person fommil    schedule 30.07.2012
comment
если быть более точным, я реализую операцию вставки, которая просто копирует некоторые элементы в вектор (ответ на событие), но проблема в том, что если есть много элементов, это блокирует поток графического интерфейса. Итак, если я понимаю ваш ответ, я должен создать новый поток и выполнить там операцию вставки? - person blejzz; 30.07.2012
comment
Хм, это сложно, потому что пользователь генерирует событие, и ваше представление отвечает на него. Если вы реализуете обработку в отдельном потоке, а затем запускаете легковесное событие (которое представление может быстро обработать), то ваши пользователи будут вынуждены испытывать небольшую задержку. С моим ограниченным пониманием вашего проекта я бы предложил рассмотреть ускорение обработки кода вашего представления, не зацикливаясь на многопоточности. - person fommil; 30.07.2012

Наоборот, это должно быть решено избеганием потока диспетчеризации событий («поток GUI» — это просто другое название того же самого). Вы должны использовать SwingWorker для запуска асинхронной задачи в отдельном потоке.

person Marko Topolnik    schedule 30.07.2012
comment
поэтому, если я правильно понимаю, EDT следует использовать только для графического интерфейса, и если мне нужны пользовательские события, мне нужно запускать события в новом потоке (и избегать EDT)? - person blejzz; 30.07.2012
comment
Вы можете выполнять любой код, который вам нравится, в EDT, если только один вызов вашего метода слушателя не займет много времени. Графический интерфейс зависает во время работы вашего обработчика. - person Marko Topolnik; 30.07.2012

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

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

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

person Lajos Arpad    schedule 30.07.2012