奇怪的线程情况 - 仅使用线程实例化变量

时间:2013-07-24 21:29:20

标签: java multithreading swing events

我正在使用OpenCYC api(不过常见,除了这一点),我正在尝试创建一个名为AccessObject的对象ao。问题是,无论出于何种原因,都无法在主Java Swing事件线程中实例化AccessObject

因此,作为一种解决方法,我创建了另一个线程,它只是在AccessObject方法中实例化run(),并为它提供了一个getter来返回它。

所以这就是我对调用代码所拥有的:

// do something with code

AccessObject ao;
AccessObjectInstantiateThread aoThread = new AccessObjectInstantiationThread();
aoThread.start();

while(ao == null) // while loop to ensure we "wait" for aoThread to finish
{
     ao = aoThread.getAoObject();
}

// Then use ao however you want

现在这段代码有效,但看起来非常荒谬。有没有更好的方法来做到这一点?请记住,我无法在主java事件线程下实例化AccessObject

非常感谢,Rich。

3 个答案:

答案 0 :(得分:4)

  1. 在SwingWorker对象中实例化它,并在完成doInBackground()方法之前检查有效对象。
  2. 将一个PropertyChangeWorker添加到侦听SwingWorker.StateValue.DONE的SwingWorker中,然后将该对象传递给Swing程序。
  3. 例如,

    class MySwingWorker extends SwingWorker<AccessObject, Void> {
      public AccessObject doInBackground() throws Exception {
        // do whatever needed to create your AccessObject and check its completion
    
        // return your AccessObject
      }
    }
    

    在你的Swing代码中:

    final MySwingWorker mySwingWorker = new MySwingWorker();
    
    mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChanged(PropertyChangeEvent pcEvt) {
        if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
          try {
            ao = mySwingWorker.get(); // ao is an AccessObject class field
    
            // you can use ao here
    
          }  catch (whaeverExceptionYouAreTrapping e) {
            // do something with exception
          }
        }
      }
    });
    mySwingWorker.execute();
    

    注意,代码注释已经过测试或编译。


    按照JB Nizet的建议

    编辑你也可以在你的Swing代码中简单地做一个匿名的内部类并跳过PropertyChangeListener:

    new SwingWorker<AccessObject, Void>() {
      public AccessObject doInBackground() throws Exception {
        // do whatever needed to create your AccessObject and check its completion
    
        // return your AccessObject
      }
    
      public void done() {
        try {
          ao = mySwingWorker.get(); // ao is an AccessObject class field
    
          // you can use ao here
    
        }  catch (whaeverExceptionYouAreTrapping e) {
          // do something with exception
        }
      }
    }.execute();
    

答案 1 :(得分:1)

您的代码可能无效。您至少应该按如下方式声明您的变量:

volatile AccessObject ao;

原因是,您的EDT线程可能会将ao变量的值缓存为优化,并且可能看不到将ao分配给新值。

我希望此代码位于应用程序的开头,用户不会看到UI没有响应。

答案 2 :(得分:0)

理想情况下,您应该在启动线程上创建AccessObject ao(在main()方法中)而不是启动您的用户界面EventQueue.InvokeLater,其中包含类似new JFrame之类的可运行文件)直到你有了。

如果失败,请使ao变得不稳定。您的实例化代码应该直接设置此值,而不是打扰“get”方法。它应该也可以调用带有runnable的InvokeLater来重新调整显示 - 可能启用一两个按钮,并向用户发送一条消息,表明现在可能无法实现的事情。

访问ao的任何代码都必须准备好它可能为空的事实;您的GUI必须以两种方式工作,使用户明白其情况。每个检查或引用应如下所示:

final AccessObject  local_ao = ao;
if (local_ao != null)  {
    // Do things.  USE local_ao, NOT oa!!!
}

简而言之,总是使用local_ao,它不会改变。请记住,ao的值可以随时更改。正如您所描述的那样,它只会从null更改为非null,并且只会执行一次,但这可能会随着代码的发展而改变。 (如果它不会发展,我的第一个建议可能是最好的。)

您的UI(EventQueue)代码不应等待任何事情。线程很痛苦,我会在UI中稍微延迟使用线程。但是你已经支付了线程的价格,所以你也可以获得你的钱。