1

私は java.net.Authenticator を使用して、プロキシ経由で接続が初めて確立されたときにユーザーにプロキシ ログイン/パスワードを要求するブロッキング ダイアログを作成していました。Authenticator は問題なく動作しますが、入力ダイアログを表示するメソッドを同期しようとしたときに奇妙な問題が発生しました。

これは、私が見つけた問題の抽象的な作業コードの例です。

private static JFrame frame;

public static void main ( String[] args )
{
    frame = new JFrame ( "Frame" );
    frame.add ( new JLabel ( "This is main application" ) );
    frame.setSize ( 500, 500 );
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setVisible ( true );

    // Cycling thread
    new Thread ( new Runnable ()
    {
        public void run ()
        {
            while ( true )
            {
                // Opening new dialog in a separate event dispatch thread
                SwingUtilities.invokeLater ( new Runnable ()
                {
                    public void run ()
                    {
                        showSomeLockingDialog ();
                    }
                } );

                // Wait 3 seconds before next window
                try
                {
                    Thread.sleep ( 3000 );
                }
                catch ( InterruptedException e )
                {
                    e.printStackTrace ();
                }
            }
        }
    } ).start ();
}

private static final Object lock = new Object ();

private static void showSomeLockingDialog ()
{
    synchronized ( lock )
    {
        // Output to see that this method is not finished yet
        System.out.println ( "Method start" );

        // Modal thread-blocking dialog
        JDialog dialog = new JDialog ( frame, "Lock" );
        dialog.add ( new JLabel ( "This should be blocking other dialogs" ) );
        dialog.pack ();
        dialog.setLocationRelativeTo ( null );
        dialog.setModal ( true );
        dialog.setVisible ( true );

        // Output to see that this method is not finished yet
        System.out.println ( "Method end" );
    }
}

だから基本的に:

  1. 目に見えるメイン フレームがあります (実際には、JVM を実行し続けるためだけに)
  2. 各サイクルで showSomeLockingDialog() メソッドを要求する循環スレッドがあります
  3. メソッドは「ロック」オブジェクトで同期されます
  4. メソッドの開始時と終了時にコンソール出力があります

したがって、この例を実行すると、前のダイアログを閉じなくても、サイクルごとに同期を無視して新しいダイアログが表示されることがわかります。代わりに単純なメソッドの同期も試しましたが、同じ効果があります。

showSomeLockingDialog() の呼び出し方法を少し変更すると、すべてが変わります。

    new Thread ( new Runnable ()
    {
        public void run ()
        {
            showSomeLockingDialog ();
        }
    } ).start ();

(イベントディスパッチスレッド内でメソッドを呼び出す代わりに、別のスレッドを使用するだけです)

このようにして、すべてが期待どおりに機能します。新しいダイアログ呼び出しは、以前に呼び出されたダイアログが閉じられるまでブロックされます。

これはかなり奇妙です。イベント ディスパッチ スレッドの何が特別で、同期が無視されるのでしょうか?

または、それが実際にバグである場合 - 回避策はありますか? 多分私は何か大きなものを見逃しています...

いくつかの考え:モーダル ダイアログの setVisible メソッドは、イベント ディスパッチ スレッド内で異なる動作をするように思えます (そうしないと、そこから呼び出された場合にインターフェイス全体がブロックされます)。しかし、それは同期にどのように影響しますか...

PSそしていいえ、私は私が望む場所でそのメソッドを呼び出していないため、私の特定のケースで2番目の(作業中の)例を使用することはできません-ランダムな場所から、主に標準のJDKクラスから呼び出されます(インターネットからロードされます - JLabel の画像、URL 入力ストリームなど)。

4

1 に答える 1

2

ダイアログのsetVisiblejavadocドキュメントから:

It is OK to call this method from the event dispatching thread because 
the toolkit ensures that other events are not blocked while this method
is blocked.

そして、Java同期ブロックが再入可能であることに基づいて、各invokeLaterで何が起こっているかを次に示します。

  1. ディスパッチスレッドが呼び出していますshowSomeLockingDialog
  2. ロックを取るか、再入場
  3. ウィンドウを開く->ダイアログを閉じるまでブロック

ブロッキングはsetVisible専用であり、他のイベント(つまり、他のinvokeLaters)用ではないため、指定された動作が得られます。

于 2012-05-17T13:48:53.660 に答える