Связь между EDT и основными потоками

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

Это сетевая программа, построенная с сервером и несколькими клиентами. У каждого клиента есть графический интерфейс, который должен действовать в соответствии с командами, отправленными с сервера. Каждый клиент заключен в класс с именем Player. Этот Player имеет графический интерфейс (расширяет JFrame) и основной метод, а сервер имеет только основной метод (без графического интерфейса). Сначала этот класс создавался в основном потоке вот так:

EventQueue.invokeLater(new Runnable()
{
    public void run()
    {
        new Player().setVisible(true);
    }
 });

Это работало нормально, пока я не понял, что весь класс Player теперь выполняется в EDT. Итак, когда я жду команд с сервера, весь графический интерфейс блокируется до тех пор, пока эта команда не будет отправлена ​​и не будут выполнены правильные действия. Как вы можете себе представить, это ужасный дизайн, который оказался настоящей головной болью среды кодирования, когда каждый раз, когда вы хотите что-то проверить, вы должны найти какой-то сумасшедший обходной путь, чтобы графический интерфейс оставался неповрежденным.

Очевидно, я должен проверять команды с сервера в отдельном потоке и запускать компоненты GUI в EDT. В моей второй реализации было два класса — один для GUI и один для Player. Идея заключалась в том, что в Player была переменная, содержащая графический интерфейс, чтобы я мог получить доступ к графическому интерфейсу из класса Player, примерно так:

class Player
{
    public GUI gui;

    ...

    // And then start this gui inside of the EDT.
    EventQueue.invokeLater(new Runnable()
    {
         public void run()
         {
              this.gui = new GUI().setVisible(true);
         }
    }

Это тоже не работает, потому что this внутри нового объекта Runnable относится к объекту Runnable, а не к Player.

Как я могу общаться между классом Player в одном потоке и соответствующим классом GUI в потоке EDT?


person Logan Serman    schedule 16.04.2009    source источник


Ответы (6)


person Adeel Ansari    schedule 16.04.2009

Чтобы решить вашу проблему с указателем this, вы должны написать:

class Player
{
    public GUI gui;

    ...

    // And then start this gui inside of the EDT.
    EventQueue.invokeLater(new Runnable()
    {
         public void run()
         {
              Playser.this.gui = new GUI().setVisible(true);
         }
    }
}
person Laurent K    schedule 16.04.2009

Борис Павлович правильно понимает синтаксис (на самом деле вы можете просто удалить this.), но код все равно не имеет смысла. Поле gui инициализируется через некоторое время после того, как событие Runnable поставлено в очередь, поэтому его использование потоком игрока небезопасно.

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

person Tom Hawtin - tackline    schedule 16.04.2009

Вы можете попробовать это:

class Player {общедоступный графический интерфейс пользователя;

...

// And then start this gui inside of the EDT.
EventQueue.invokeLater(new Runnable()
{
     public void run()
     {
          Player.this.gui = new GUI().setVisible(true);
     }
}
person Boris Pavlović    schedule 16.04.2009

«пока я не понял, что весь класс Player теперь выполняется в EDT»

Конструктор встречается в EDT, но методы, вызываемые в этом классе, могут отсутствовать.

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

 EventQueue.invokeLater(new Runnable() 
 {
    public void run()
    {
        new Player().setVisible(true);
    }
 });

Но Player может запустить отдельный поток в конструкторе (лично я бы разделил связь между Players).

Конечно, методы обратного вызова с сервера должны использовать invokeLater() при изменении видимых компонентов.

person Pool    schedule 16.04.2009

Вместо использования анонимного внутреннего класса, почему бы просто не объявить класс, реализующий Runnable, и иметь конструктор, который принимает экземпляр GUI в качестве аргумента?

Кроме того, если ваш класс GUI не является потокобезопасным, рассмотрите возможность использования очереди сообщений для связи между EDT и основным потоком.

person oscarkuo    schedule 16.04.2009