Почему EDT не закрывается, когда все живые потоки являются демонами? - PullRequest
1 голос
/ 09 февраля 2010

Следующий код перемещает карту по экрану. Когда я закрываю главное окно, я ожидаю, что поток диспетчеризации событий также отключится, но это не так. Любые идеи о том, почему поток ScheduledExecutorService предотвращает закрытие EDT?

import java.awt.Graphics;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main extends JPanel
{
  private float x = 1;

  public void next()
  {
    x *= 1.1;
    System.out.println(x);
    repaint();
  }

  @Override
  protected void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    URL url = getClass().getResource("/209px-Queen_of_diamonds_en.svg.png");
    g.drawImage(new ImageIcon(url).getImage(), (int) x, 50, null);
  }

  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    final Main main = new Main();
    frame.getContentPane().add(main);
    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setVisible(true);

    ScheduledExecutorService timer = Executors.newScheduledThreadPool(1, new ThreadFactory()
    {
      public Thread newThread(Runnable r)
      {
        Thread result = new Thread(r);
        result.setDaemon(true);
        return result;
      }
    });
    timer.scheduleAtFixedRate(new Runnable()
    {
      public void run()
      {
        SwingUtilities.invokeLater(new Runnable()
        {
          public void run()
          {
            main.next();
          }
        });
      }
    }, 100, 100, TimeUnit.MILLISECONDS);
  }
}

Ответы [ 4 ]

3 голосов
/ 09 февраля 2010

Поведение по умолчанию при закрытии JFrame просто скрывает его, а не заставляет приложение закрываться. Вам нужно позвонить:

frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

Другими словами: это не имеет ничего общего с ScheduledExecutorService; Это связано с тем, что поток Event Dispatch не является потоком демона.

ДОПОЛНИТЕЛЬНЫЙ

Вместо использования ScheduledExecutorService, который, в свою очередь, вызывает SwingUtilities.invoke..., вам следует рассмотреть возможность использования javax.swing.Timer, который будет периодически запускать ActionEvent s непосредственно в потоке диспетчеризации событий , следовательно, делая ваш код проще / компактнее и избавляет от необходимости в дополнительной резьбе.

Кроме того, вы воссоздаете ImageIcon на каждом кадре анимации, что будет очень неэффективно, особенно в тесном цикле анимации. Гораздо лучше создать его один раз при запуске приложения.

1 голос
/ 11 февраля 2010

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

  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    final Main main = new Main();
    frame.getContentPane().add(main);
    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setVisible(true);

    final ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
    timer.scheduleAtFixedRate(new Runnable()
    {
      public void run()
      {
        // NOTE that you don't need invokeLater here because repaint() is thread-safe
        main.next();
      }
    }, 100, 100, TimeUnit.MILLISECONDS);
  }
  // Listen to main frame closure and shut down timer
  main.addWindowListener(new WindowAdapter()
  {
      public void windowClosed(WindowEvent e)
      {
          timer.shutdownNow();
      }
  });
}

Обратите внимание на изменения, которые я внес в ваш фрагмент:

  1. timer теперь объявлено final (необходимо как на него ссылается внутренний анонимный класс)
  2. Больше нет ThreadFactory передано newScheduledThreadPool
  3. Я удалил использование invokeLater для вызова main.next() потому что единственный Свинг сделанный звонок repaint() который является одним из немногих методов Swing, которые Потокобезопасны.

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

1 голос
/ 11 февраля 2010

Я наткнулся на ответ в этом отличном сообщении в блоге: http://www.pushing -pixels.org /? P = 369

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

  • Нет отображаемых компонентов AWT или Swing.
  • В собственной очереди событий нет собственных событий.
  • В java EventQueues нет событий AWT.

[...]

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

Далее автор говорит, что его код отправляет событие в EDT каждые 100 мс, несмотря на то, что соответствующее окно больше не отображается. Это именно то, что происходит и в моем случае! ScheduledExecutorService публикует события в EDT, что, в свою очередь, предотвращает отключение AWT, что, в свою очередь, означает, что ScheduledExecutorService будет продолжать публиковать больше событий.

Кроме того, я удивлен количеством людей, которые рекомендуют использовать JFrame.EXIT_ON_CLOSE. Я полагаю, каждому свое, но я рекомендую прочитать http://findbugs.sourceforge.net/bugDescriptions.html#DM_EXIT

1 голос
/ 09 февраля 2010

Ваша фабрика нитей верна. Если вы установите EXIT_ON_CLOSE в кадре, он выйдет.

Однако рассмотрите возможность использования библиотеки, такой как Trident .

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