在我的java应用程序中,我有JTabbedPane一个主要JFrame有很多JPanel选项卡.在标签内我有一个功能
public void newStatus(final String arg)
{
Runnable sendMsg = new Runnable()
{
@Override
public void run()
{
mainView.newStatusLine(arg);
}
};
SwingUtilities.invokeLater(sendMsg);
}
Run Code Online (Sandbox Code Playgroud)
此函数调用main JFrame mainView函数将一些文本写入a JTextPane.我的问题是,这不允许我从main获得返回值JFrame.我想做点什么
public InfoObj getInfo()
{
Runnable sendMsg = new Runnable()
{
@Override
public void run()
{
return mainView.getInfo();
}
};
SwingUtilities.invokeLater(sendMsg);
}
Run Code Online (Sandbox Code Playgroud)
但我不确定如何使这项工作.我已经尝试并按照我的IDE的消息来查看我是否可以让它可能工作,但我无法覆盖Runnable.Run以返回一些东西.
有没有什么机制可以做这样的事情?
编辑:对于HoverCraftFullOfEels,整体问题是JPanels他们之间以及主要JFrame和a 之间的交谈JPanel.在某些时候,JPanel想要告诉主要JFrame做某事,或者它可能想从中获取一些数据.但是,从我知道的一切,我不能只传递一个参考this的任一JFrame或JPanel并用它来调用公共函数或无论是读一些公共领域.
有时我想通过生成的线程在EDT上执行此操作.一些JPanel生成线程,我想将JPanels引用传递给main,JFrame因此它可以调用其中的函数来告诉用户线程正在做什么.
看起来您的应用程序数据都绑定在用户界面对象中.如果您所做的一切都可以在EDT上完成,那么您应该没问题.您可以在需要彼此信息的对象之间进行直接调用.由于所有这些都在EDT上,因此它实际上是单线程的,并且没有竞争条件或死锁.然而,这可以将UI代码的各个部分彼此紧密地耦合在一起.在这种情况下,您可以使用某些观察者或听众模式,如评论者所建议的那样.这在像AWT/Swing这样的单线程,事件驱动的环境中工作正常.
但是你说你可能想要从EDT以外的其他一些线程中做到这一点.这改变了一切.
(顺便说一句,这种情况会导致人们考虑将所有应用程序数据绑定在UI对象中作为反模式.这使得从UI子系统外部获取此数据变得非常困难. )
您可以添加观察者模式,但存在限制.UI拥有数据,因此只有EDT可以更改数据并向观察者发送事件.观察者列表需要以线程安全的方式进行维护.据推测,观察者将希望更新数据结构以响应事件.这些数据结构必须是线程安全的,因为它们既可以从EDT访问,也可以从其他线程访问.这种方法可行,但您必须考虑哪个线程正在调用哪些方法,以及哪些数据结构必须是线程安全的.
假设你不想走这条路,让我们回到你原来的问题,这是关于如何从中返回的东西invokeLater.这样做可以让您将数据保留在UI中,同时允许其他线程在需要时从UI中获取数据.这是可能做到这一点,有一点间接的.但它确实存在风险.
这是代码.这可以从"其他"(非EDT)线程调用.
InfoObj getInfo() {
RunnableFuture<InfoObj> rf = new FutureTask<>(() -> getInfoObjOnEDT());
SwingUtilities.invokeLater(rf);
try {
return rf.get();
} catch (InterruptedException|ExecutionException ex) {
ex.printStackTrace(); // really, you can do better than this
}
}
Run Code Online (Sandbox Code Playgroud)
这里的技巧是,RunnableFuture是,是一个接口都一Runnable和Future.这很有用,因为invokeLater只需要一个Runnable,但Future可以返回一个值.更具体地说,Future可以捕获在一个线程中返回的值并将其存储直到另一个线程想要获取它.FutureTask是一个方便的,现成的实现,RunnableFuture它接受一个Callable参数,一个可以返回值的函数.
基本上我们创建一个FutureTask并Callable在EDT上运行一些代码(在本例中为lambda表达式).这将收集我们想要的信息并将其返回.然后我们使用将其发布到EDT invokeLater.当另一个线程想要数据时,它可以get()立即调用,也可以挂起Future并get()稍后调用.有一个小小的不便之处在于Future.get()抛出一些必须处理的异常检查异常.
这是如何运作的?如果EDT运行第Callable一个,则返回值存储在Future另一个线程调用get()它之前.如果另一个线程get()首先调用,它将被阻塞,直到该值可用.
这就是问题所在.精明的读者会认识到这具有相同的风险invokeAndWait(),即,如果你不小心,你可以使系统陷入僵局.这可能是因为调用的线程get()可能会阻止等待EDT处理发布的事件invokeLater.如果EDT由于某种原因被阻塞 - 可能正在等待另一个线程持有的东西 - 结果就是死锁.避免这种情况的方法是在调用可能阻止等待EDT的事情时要非常小心.一个好的一般规则是在调用其中一个阻塞方法时不要持有任何锁.
有关如何让自己陷入麻烦invokeAndWait或与之相遇的示例invokeLater(FutureTask),请参阅此问题及其答案.
如果您的其他线程完全与UI数据结构分离,则此技术应该非常有效.