SwingUtilities.invokeLater берет Runnable и запускает его на EDT?

Меня смущает подпись SwingUtilities.invokeLater, Требуется Runnable объект. Это объект Runnable, который передается в поток диспетчеризации событий? Почему я не могу позвонить напрямую createAndShowGUI на run метод EDT (если это возможно)?

Я читал статьи на SO о том, как работают EDT и invokeLater, но меня смущает Runnable объект, который передается.

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});

И что будет, если я позвоню SwingUtilities.invokeLater снова прямо под вызовом?

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        doSomethingOnTopOfGUI();
    }
});

2 ответа

Решение

Что такое новый Runnable() {}?

new Runnable() {
    public void run() {
        createAndShowGUI();
    }
};

Это объявляет анонимный класс и создает его новый экземпляр. Это в основном эквивалентно этому:

class Creator implements Runnable {
    public void run() {
        createAndShowGUI();
    }
}

new Creator();

До лямбды Java 8 анонимный класс - это способ выполнять функциональное программирование. Передача Runnable в invokeLater это как передача функции, которую EDT может выполнить позже, когда захочет (путем вызова run в теме).

Что invokeLater делает с Runnable?

Изначально все, что он делает, - это создает событие, чтобы обернуть его ( InvocationEvent) и поместить его в конец очереди.

После постановки в очередь, invokeLater уведомляет EDT (который просыпается, если ожидал). Собственный метод выполнения EDT представляет собой бесконечный цикл, который обрабатывает события. Это выглядит примерно так (очень перефразировано):

public void run() {
    while(true) {
        EventQueue eq = getEventQueue();

        synchronized(eq) {
            while(eq.hasNextEvent()) {
                processOneEvent(eq.getNextEvent());
            }

            try {
                eq.wait();
            } catch(InterruptedException ie) {}
        }
    }
}

Когда EDT добирается до нового InvocationEvent, он вызывает run на Runnable, который вызывает createAndShowGUI,

Таким образом, передавая Runnable для invokeLater или же invokeAndWait позволяет запускать любой код, который вы хотите на EDT.

Как скоро пробежка называется?

В большинстве случаев сразу или почти сразу. EDT очень отзывчивый. "Поздняя" часть "invokeLater" нам подсказывает, что:

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

Конечно invokeAndWait существует в качестве альтернативы, если синхронный вызов run желательно (Хотя следует отметить, что invokeAndWait может также вызвать обработку других событий, если они были поставлены в очередь перед новым InvocationEvent.)

Асинхронный

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        System.out.println("hello");
    }
});
System.out.println("world");

Это может распечатать

Привет
Мир

и это может напечатать

Мир
Привет

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

синхронный

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        System.out.println("hello");
    }
});
System.out.println("world");

Это обязательно напечатает

Привет
Мир

потому что вызывающий поток будет ждать run завершить до продолжения (если не выдается исключение).

invokeAndWait использует схему ожидания и уведомления для достижения этой цели. Объект монитора создается и передается в InvocationEvent. invokeAndWait звонки wait после публикации события и вызовов InvocationEvent notifyAll после run завершает на EDT. Схема ожидания и уведомления является причиной invokeAndWait не может быть вызвано на EDT. У EDT нет возможности остановиться в середине одного события для обработки другого.

Что произойдет, если в очереди будет находиться более одного события?

EDT обрабатывает события по одному, в порядке их очередей и в соответствии с их приоритетом. Некоторые события также могут быть объединены (обычное InvocationEvent не будет). Большинство событий имеют нормальный приоритет. PaintEvent (как от звонка repaint) обычно имеет низкий приоритет, а некоторые системные события имеют более высокий приоритет.

В конкретном примере вы дали:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        doSomethingElse();
    }
});

Поскольку они относятся к одному виду событий и имеют одинаковый приоритет, doSomethingElse событие будет обработано после createAndShowGUI событие сделано.

Точно так же, если что-то подобное было сделано:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                System.out.println("world");
            }
        });
        System.out.println("hello");
    }
});

Это будет печатать

Привет
Мир

поскольку world Runnable запускается после hello Runnable завершается.

Ты можешь использовать SwingUtilities.invokeLater() из любого потока, чтобы поставить задачу в очередь на EDT. Это полезно в некоторых ситуациях, особенно в многопоточных приложениях, в качестве удобного способа обновления элементов графического интерфейса из потоков, не относящихся к графическому интерфейсу, а также при запуске приложения (на что намекает ваш пример) из main(), который запускается в основном потоке, а не в EDT.

Например:

// main is not executed in the EDT
public static void main (String[] args) {

    // create and initialize GUI in the EDT to avoid potential thread-safety issues.
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });

}

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

Другие вопросы по тегам