2011-07-08 4 views
3

私の読んだJLS 12.5は、コードサンプル内のアサーションが決して—をトリガーすべきではないと思うが、私のマルチスレッドのコードでは起こりません。 (JLSは、このセクションでスレッディングを指定していません)。しかし、私の読みが正しいかどうかは、ポイントの横にある。私はこれを常に真実にしたいと思っています。スレッドの存在下でオブジェクトの完全な構成を保証するには

public class MainWindow extends JFrame { 
    private final JLabel label; 

    public MainWindow() { 
    label = new JLabel(); 
    pack(); 
    setVisible(true); 
    } 

    public JLabel getLabel() { 
    Assert.assertNotNull(label); 
    return label; 
    } 
} 

明白な答えは同期ブロックにコンストラクタの内臓を包むようにし、同様に同期ゲッターをマークすることです。 良い方法がありますか?

private MainWindow findMainWindow() { 
    for (Frame frame : Frame.getFrames()) { 
     if (frame instanceof MainWindow) { 
      return (MainWindow)frame; 
     } 
    } 
    return null; 
} 

は(ところで、私はMac上でJDK6を実行している)

更新:

FWIWは、他のスレッドは、JUnitテスト内でこのコードでウィンドウへの参照を取得しています:

私はそれを同期しようとしましたが、それでも動作しません。

public class MainWindow extends JFrame { 
    private final JLabel label; 
    public MainWindow() { 
    synchronized(this) { 
     label = new JLabel(); 
    } 
    } 

    public synchronized JLabel getLabel() { 
    Assert.assertNotNull(label); 
    return label; 
    } 
} 

アップデート2:ここでは、コードだ

ここでそれを修正変更です:

private MainWindow findMainWindow() throws Exception { 
    final AtomicReference<MainWindow> window = new AtomicReference<MainWindow>(); 
    SwingUtilities.invokeAndWait(new Runnable() { 
     public void run() { 
      for (Frame frame : Frame.getFrames()) { 
       if (frame instanceof MainWindow) { 
        window.set((MainWindow) frame); 
        return; 
       } 
      } 
     } 
    }); 
    return window.get(); 
} 
+2

を);のsetVisible(true)を'ctorから別のメソッドへ - ' this'は現在他のスレッドにエスケープされているようです。 –

+0

私の更新では、これらのメソッドを削除しました。まだ行きません。 –

+2

は 'Frame.getFrames()'でもEDTの外で呼び出しても安全ですか? – jtahlborn

答えて

1

(注:この回答のオリジネーターの場合は1実際にそれに答えると、私はこの回答を受け入れずに代わりに受け入れます)。

窓枠はEDTから建設されています。これはスイングポリシーに対してすべてアクセス—(—を含む)がEDTで発生するはずです。私は今、この(私のEventQueue utility classを使用して)のようなウィンドウを作成

:私はスイングの内臓を知らないが、私は問題は( `パックを動かすことによって解決されるだろう示唆

MainWindow ui = EventQueue.invokeAndGet(new Callable() { 
    public MainWindow call() { 
     return new MainWindow(...); 
    } 
}); 
0

一つの方法は、コンストラクタをプライベートにすることで、リターンにファクトリメソッドを提供するかもしれません新しいインスタンス。

何かのように:

public static final Object lockObject = new Object(); 
public static final MainWindow createWindowInstance() { 
    synchronized(lockObject) { 
    MainWindow win = new MainWindow(); 
    return win; 
    } 
} 

これは、コードがより多くの擬似コードであるように、私の頭の上からです。

+0

問題は実際には、コンストラクタが戻る前にウィンドウにアクセスしていることです。根本原因:EDTにウィンドウが作成されていない。 EDT(建設を含む)へのすべてのアクセスを移動することで問題は解決されます。 –

1

「ラベル」の値が「getLabel」にあることを保証する方法はありません。まあ、実際には私はいくつかあると思いますが、それは問題に近づくための間違った方法です。

問題は、どこかのようなインスタンスフィールドの宣言持っていることです。

private MainWindow mainWindow; 

とどこか(と、それはより良いEventQueueの上で実行するか、またはあなたが他のトラブルを持っています)のような文:

mainWindow = new MainWindow(); 
開始は、「メインウィンドウ」を実行するには、この声明一度

は、メイン・ウィンドウオブジェクトのデータが置かれるスペースへの非null参照を持っています。原因別のスレッドで、この時点で、すべてのマルチスレッドプログラムを悩ませて不運に次のコードが実行されます:

MainWindow mw = mainWindow; 
if (mw != null) label = mw.getLabel(); 

あなたのアサーションがトリガされます。その後、元のスレッドは、コンストラクターコードが実行されている間は非常に慎重にロックされます。

解決策: "mainWindow"を揮発性にします。これにより、mainWindowがその値を取得する前に、MainWindowオブジェクトが完了していることをコンパイラーが強制的に確認します。また、それは必要ありませんが、私は絶対的な最小値にインスタンスフィールドへの参照を維持するために、できるだけシンプルにそれらを維持したいので、私はこのように見ているコードを持っていると思います:

private volatile MainWindow mainWindow; 

MainWindow mw = new MainWindow(); 
mainWindow = mw; 

は、これを行います同様のすべてのケースで。 複数のスレッドからアクセスされるインスタンスまたはクラスフィールドに値を割り当てるときは、割り当てようとしているものがほかのすべてのスレッドで参照できる状態になっていることを確認してください。

(あなたもEventQueueのへの呼び出しに「getLabel」を得ることができる場合、あなたはこのすべてを忘れると、シングルスレッドの至福に生きることができます。)

関連する問題