Накопительный Runnable в Swing Java - PullRequest
0 голосов
/ 21 февраля 2020

В рамках изучения SwingWorker я изучал исходный код, где заметил нечто, называемое AccumulativeRunnable. Из комментария к определению класса AccumulativeRunnable я понимаю, когда его следует использовать, но когда я проверял код примера, у меня возникли несколько вопросов.

Ниже приведен комментарий и пример кода в абстрактном классе AccumulativeRunnable.

Абстрактный класс (AccumulativeRunnable) для использования в тех случаях, когда нам нужно выполнить некоторые действия над добавляемым набором данных.
Набор данных может быть добавлен после того, как он отправлен для выполнение. Обычно такие Runnables отправляются в EDT.

Пример использования: Скажем, мы хотим реализовать JLabel.setText (текст String), который отправляет строку в JLabel.setTextImpl (текст String) на ПО ВОСТОЧНОМУ ВРЕМЕНИ. В случае, если JLabel.setText быстро вызывается много раз с EDT, мы получим много обновлений на EDT, но важен только последний. (Каждое следующее обновление отменяет предыдущее.) Мы можем захотеть реализовать это таким образом, чтобы доставлялось только последнее обновление.

  AccumulativeRunnable<String> doSetTextImpl =
  new  AccumulativeRunnable<String>()} {

      protected void run(List<String> args)} {
          //set to the last string being passed
          setTextImpl(args.get(args.size() - 1));
      }
  }

  void setText(String text) {
      //add text and send for the execution if needed.
      doSetTextImpl.add(text);
  }

Вопросы

  1. абстрактный класс AccumulativeRunnable implements Runnable. Это означает, что класс AccumulativeRunnable должен реализовывать метод run, верно? Но я мог видеть только protected abstract void run(List<T> args);. Как это может быть реализацией интерфейса Runnable.
  2. Почему метод add() класса AccumulativeRunnable равен synchronized? Может кто-нибудь объяснить это на простом примере или на примере, который я привел выше.
  3. Когда arguments внутри add() метода класса AccumulativeRunnable будет null? Может кто-нибудь объяснить это на простом примере или на примере, который я привел выше.
  4. Как метод add() получает массив (T ... args)? Может ли кто-нибудь объяснить это на простом примере или на примере, который я привел выше.
  5. В приведенном выше примере, когда мы вызываем doSetTextImpl.add(text);, он вызывает метод add() класса AccumulativeRunnable. Но как это внутренне вызывать метод run()? Я имею в виду, кто вызывает наш реализованный метод run() внутри.

Весь код в AccumulativeRunnable:

public abstract class AccumulativeRunnable<T> implements Runnable {
    private List<T> arguments = null;

    /**
     * Equivalent to {@code Runnable.run} method with the
     * accumulated arguments to process.
     *
     * @param args accumulated argumets to process.
     */
    protected abstract void run(List<T> args);

    /**
     * {@inheritDoc}
     *
     * <p>
     * This implementation calls {@code run(List<T> args)} mehtod
     * with the list of accumulated arguments.
     */
    public final void run() {
        run(flush());
    }

    /**
     * appends arguments and sends this {@code Runnable} for the
     * execution if needed.
     * <p>
     * This implementation uses {@see #submit} to send this
     * {@code Runnable} for execution.
     * @param args the arguments to accumulate
     */
    @SafeVarargs
    @SuppressWarnings("varargs") // Copying args is safe
    public final synchronized void add(T... args) {
        boolean isSubmitted = true;
        if (arguments == null) {
            isSubmitted = false;
            arguments = new ArrayList<T>();
        }
        Collections.addAll(arguments, args);
        if (!isSubmitted) {
            submit();
        }
    }

    /**
     * Sends this {@code Runnable} for the execution
     *
     * <p>
     * This method is to be executed only from {@code add} method.
     *
     * <p>
     * This implementation uses {@code SwingWorker.invokeLater}.
     */
    protected void submit() {
        SwingUtilities.invokeLater(this);
    }

    /**
     * Returns accumulated arguments and flashes the arguments storage.
     *
     * @return accumulated arguments
     */
    private synchronized List<T> flush() {
        List<T> list = arguments;
        arguments = null;
        return list;
    }
}

1 Ответ

1 голос
/ 21 февраля 2020
  1. Ответ - следующая реализация Runnable.run(). С точки зрения компилятора, run(List<T>) не имеет ничего общего с методом, объявленным интерфейсом, это просто другой метод с (по совпадению) тем же именем.

    publi c final void run ( ) {беги (грипп sh ()); }

  2. В графических средах много параллелизма, и synchronized предотвращает вызов метода из двух потоков одновременно, в противном случае вы бы создали так называемое состояние гонки, при котором обновление «более быстрого» потока в списке просто теряется. В этом конкретном c случае это условие гонки могло бы возникнуть, если бы add(T...) отсутствовал в *1015*, и два потока одновременно пытались добавить первый элемент в список.

  3. До того, как первый элемент был добавлен через add(T). arguments - список всех операций, которые должны быть выполнены. Если вы создаете новый AccumulativeRunnable<T>, атрибут arguments будет null (см. Строку 2), пока не будет добавлен первый элемент.

  4. T... называется "аргумент varargs". По сути, это всего лишь синтаксический сахар, и он позволяет вам звонить add любым из следующих способов (для получения дополнительной информации читайте this ):

    1. add(firstObject). Это внутренне преобразует один предоставленный вами объект в массив типа T с одним элементом (firstObject).

    2. add(firstObject, secondObject, thirdObject) и т. Д. С любым количеством аргументов. Все эти аргументы будут упакованы в один массив и переданы в функцию.

    3. add(objectArray), причем objectArray - это действительный массив типа T. В этом случае внутренняя переменная arguments будет просто ссылаться на предоставленный массив.

  5. Ответ записывается в предоставленной вами цитате:

    Обычно такие Runnables отправляются в EDT.

    EDT = поток рассылки событий, «скрытый» поток где-то глубоко внутри инфраструктуры Swing, который обрабатывает все нажатия кнопок и т. д. c. То, что может вызвать метод run(), это, например, вызов frame.paint() (или как бы этот метод ни вызывался, я использую JFX, поэтому я не эксперт по Swing), кнопка щелчок, движение мыши и т. д. c.

...