1

次のコードは、カードを画面上でスライドさせます。メインウィンドウをシャットダウンすると、イベントディスパッチスレッドもシャットダウンするはずですが、そうではありません。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

4 に答える 4

3

aを閉じたときのデフォルトの動作JFrameは、アプリケーションを終了させるのではなく、単にそれを非表示にすることです。電話する必要があります:

frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

言い換えれば、これはScheduledExecutorService;とは何の関係もありません。これは、イベントディスパッチスレッドがデーモンスレッドではないという事実と関係があります。

追加

ScheduledExecutorServiceを使用するのではなく、を使用するSwingUtilities.invoke...ことを検討する必要がありますjavax.swing.Timer。これにより、イベントディスパッチスレッドで直接ActionEvent定期的にsが起動されるため、コードがよりシンプル/コンパクトになり、追加のスレッドが不要になります。

また、ImageIcon特にタイトなアニメーションループでは、非常に非効率的なすべてのアニメーションフレームでを再作成しています。アプリケーションの起動時に一度作成する方がはるかに優れています。

于 2010-02-09T17:41:31.273 に答える
1

私はこの素晴らしいブログ投稿で答えに出くわしました:http ://www.pushing-pixels.org/?p = 369

現在の実装では、AWTはすべてのヘルパースレッドを終了し、次の3つの条件が当てはまる場合にアプリケーションを正常に終了できるようにします。

  • 表示可能なAWTまたはSwingコンポーネントはありません。
  • ネイティブイベントキューにネイティブイベントはありません。
  • javaEventQueuesにはAWTイベントはありません。

[...]

現在の実装では、このタイムアウトは1000ミリ秒(または1秒)です。これは事実上、アプリケーションの最後のウィンドウを破棄し、保留中のすべてのイベントを処理した直後にAWTがシャットダウンされないことを意味します。代わりに、毎秒ウェイクアップし、スリープ中に保留中または処理済みのイベントをチェックし、そのようなイベントが発生した場合はスリープを継続します。

著者は、関連するウィンドウが表示されなくなったにもかかわらず、コードが100ミリ秒ごとにEDTにイベントを投稿すると続けています。これはまさに私の場合にも起こります!ScheduledExecutorServiceはEDTにイベントを投稿します。これにより、AWTがシャットダウンするのを防ぎます。つまり、ScheduledExecutorServiceはさらに多くのイベントを投稿し続けます。

余談ですが、の使用を勧める人が多いことに驚いていますJFrame.EXIT_ON_CLOSE。それぞれが彼自身のものだと思いますが、http://findbugs.sourceforge.net/bugDescriptions.html#DM_EXITを読むことをお勧めします

于 2010-02-11T04:08:26.357 に答える
1

でデーモンスレッドを使用するのではなく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. スレッドセーフな数少ないSwingメソッドの1つであるSwing呼び出しのみが行われるため、invokeLater呼び出し の使用を削除しました。main.next()repaint()

上記のコードは試していません。コンパイルする必要があり、問題も解決すると思います。それを試して、私たちに知らせてください!

于 2010-02-11T17:40:08.447 に答える
1

スレッドファクトリは正しいです。EXIT_ON_CLOSEフレームに設定すると、終了します。

ただし、代わりにTridentなどのライブラリを使用することを検討してください。

于 2010-02-09T19:15:30.000 に答える