Java
 Computer >> コンピューター >  >> プログラミング >> Java

Java Swingはスレッドセーフではない?その理由とEDTの正しい使い方を解説

結論から言うと、Java Swingのコンポーネントはスレッドセーフではありません。複数のスレッドから同時にSwingコンポーネントへアクセスすると、描画の不整合やデッドロックなど予期しない動作を引き起こす可能性があります。

Swingがスレッドセーフではない理由

  • 最大の理由は、コンポーネントの拡張(サブクラス化)を簡単にするためです。すべてのメソッドで同期処理を行うと、開発者がオーバーライドして独自のコンポーネントを作る際に、ロックの扱いまで意識する必要が生じ、設計が複雑になってしまいます。
  • もう一つの理由は、ロックの取得・解放や状態の復元にかかるオーバーヘッドです。全メソッドをスレッドセーフにするとパフォーマンスが大幅に低下するため、あえて排他制御を省略しています。
  • ただし、例外としてJComponentクラスのrepaint()、revalidate()、invalidate()などの一部のメソッドは、マルチスレッドからのアクセスがサポートされています。

イベントディスパッチスレッド(EDT)とは

Swingコンポーネントが画面への描画可能な状態になると、そのコンポーネントにはイベントディスパッチスレッド(Event Dispatch Thread:EDT)からのみアクセスできます。EDTは、paint()やupdate()といったコールバックメソッドや、各種イベントリスナーインターフェースで定義されたイベントハンドラメソッドを呼び出す専用のスレッドです。

スレッドセーフとされているメソッドであればどのスレッドからでも安全に呼び出せますが、Swingオブジェクトのほとんどのメソッドはスレッドセーフではないため、単一のスレッドであるEDT上でのみ実行する必要があります。

コード例:EDTを使ったGUI生成

import javax.swing.*;
import java.awt.Dimension;
import java.awt.event.*;

public class EDTTest extends JPanel implements ActionListener {
    private static EDTTest myPanel;
    private static JFrame myFrame;

    public EDTTest() {
        super();
    }

    public Dimension getPreferredSize() {
        return new Dimension(500, 425);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        myFrame = new JFrame("EDT Program");
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myFrame.setLocationRelativeTo(null);
        myPanel = new EDTTest();
        myFrame.add(myPanel);
        initMenu();
        myFrame.setVisible(true);
    }

    private static void initMenu() {
        JMenuBar menuBar = new JMenuBar();
        myFrame.setJMenuBar(menuBar);
        JMenu file = new JMenu("File");
        JMenuItem quit = new JMenuItem("Quit");
        quit.addActionListener(myPanel);
        file.add(quit);
        menuBar.add(file);
    }

    public void actionPerformed(ActionEvent ae) {
        String choice = ae.getActionCommand();
        if (choice.equals("Quit")) {
            System.exit(0);
        }
    }
}

この例では、SwingUtilities.invokeLater()メソッドを使用しています。このメソッドはGUI生成のタスクをキューに登録し、初期スレッド(mainスレッド)の処理が完了した後に、確実にEDT上でGUIの構築が行われるようにします。これにより、Swingの「シングルスレッドルール」に従った安全なアプリケーションになります。

実行結果

Java Swingはスレッドセーフではない?その理由とEDTの正しい使い方を解説

まとめ

Swingはスレッドセーフではないため、GUIの構築・更新は必ずEDT上で行うのが基本です。時間のかかる処理は別スレッド(SwingWorkerなど)で実行し、結果をUIに反映する際だけinvokeLater()やinvokeAndWait()を使ってEDTに戻る、という設計を心がけましょう。

  1. Java Swingのアーキテクチャとは?特徴とMVCモデルをわかりやすく解説

    Java Swingは、Javaプログラム向けにグラフィカルユーザーインターフェース(GUI)を提供するAPI群です。Java Swingは、それ以前のAPIであるAbstract Window Toolkit(AWT)をベースに開発されました。AWTと比べて、より豊富で洗練されたGUIコンポーネントを備えており、シンプルな部品から複雑なツリーやテーブルまで幅広く利用できます。さらに、プラグイン可能なルック&フィール(Pluggable Look and Feel)機能により、Javaプログラムの外観を基盤となるプラットフォームから独立して制御できる点も大きな特長です。 Java Swingの

  2. JavaのSwingコンポーネントが軽量なのに、AWTコンポーネントはなぜ重量級なのか?

    AWT(Abstract Window Toolkit)は、JavaのGUIプログラミングを支える基本的なツールキットで、スタンドアロンのJavaアプリケーションやアプレット向けに設計された移植性の高いGUIライブラリです。AWTはアプリケーションとネイティブGUI(OSが提供するウィジェット)をつなぐ架け橋の役割を担います。 一方、Java SwingはAWTの技術をベースに構築されたGUIコンポーネント群であり、「プラグイン可能なルック・アンド・フィール(Pluggable Look and Feel)」を提供します。最大の特徴は、Swingが完全にJava言語のみで実装されているという点