在我的java应用程序中,我在主JTabbedPane
上有JFrame
个JPanel
个标签。在标签内我有一个功能
public void newStatus(final String arg)
{
Runnable sendMsg = new Runnable()
{
@Override
public void run()
{
mainView.newStatusLine(arg);
}
};
SwingUtilities.invokeLater(sendMsg);
}
此函数调用主JFrame mainView
函数将一些文本写入JTextPane
。我的问题是,这不允许我从主JFrame
获得返回值。我想做点什么
public InfoObj getInfo()
{
Runnable sendMsg = new Runnable()
{
@Override
public void run()
{
return mainView.getInfo();
}
};
SwingUtilities.invokeLater(sendMsg);
}
但我不确定如何使这项工作。我已经尝试并按照我的IDE的消息来查看我是否可以将其用于工作,但我无法覆盖Runnable.Run
以返回某些内容。
是否有任何机制可以做这样的事情?
编辑:对于HoverCraftFullOfEels,整体问题是在JPanels
之间以及主JFrame
和JPanel
之间进行讨论。在某些时候,JPanel
想要告诉主JFrame
做某事,或者它可能想从中获取一些数据。但是从我知道的所有内容中,我不能只将this
的引用传递给JFrame
或JPanel
,并使用它来调用公共函数或读取其中的一些公共字段。
有时我想通过生成的线程在EDT上执行此操作。一些JPanel
生成线程,我想将JPanel
的引用传递给主JFrame
,以便它可以调用其中的函数来告诉用户线程正在做什么。
答案 0 :(得分:6)
看起来您的应用程序数据都绑定在用户界面对象中。如果您所做的一切都可以在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
}
}
这里的诀窍是RunnableFuture
是一个 一个Runnable
和一个Future
的界面。这很有用,因为invokeLater
只需Runnable
,但Future
可以返回一个值。更具体地说,Future
可以捕获在一个线程中返回的值并将其存储,直到另一个线程想要获取它。 FutureTask
是一个方便的,现成的RunnableFuture
实现,它带有一个Callable
参数,一个可以返回值的函数。
基本上我们创建一个FutureTask
并在EDT上运行一些代码(Callable
,这个例子中的lambda表达式)。这将收集我们想要的信息并将其返回。然后我们使用invokeLater
将其发布到EDT。当另一个线程需要数据时,它可以立即调用get()
,也可以挂在Future
上并稍后调用get()
。由于Future.get()
会引发一些您必须处理的异常检查异常,因此会有一些不便。
这是如何工作的?如果EDT首先运行Callable
,则返回值将存储在Future
中,直到另一个线程在其上调用get()
。如果另一个线程首先调用get()
,它将被阻塞,直到该值可用。
这就是问题所在。精明的读者会认识到这与invokeAndWait()
具有相同的风险,即,如果你不小心,你可以使系统陷入僵局。这可能是因为调用get()
的线程可能会阻止等待EDT处理invokeLater
发布的事件。如果由于某种原因EDT被阻塞 - 可能正在等待另一个线程持有的东西 - 结果就是死锁。避免这种情况的方法是在调用可能阻止等待EDT的事情时要非常小心。一个好的一般规则是在调用其中一个阻塞方法时不要保持任何锁定。
有关如何使用invokeAndWait
或invokeLater(FutureTask)
让自己遇到问题的示例,请参阅this question和my answer。
如果你的其他线程完全与UI数据结构分离,这种技术应该非常有效。
答案 1 :(得分:0)
这对我有效 JAVA7
//Here the main thread invokes it
final RunnableFuture<Data> task = new FutureTask<Data>( new Callable<Data>() {
@Override
public Data call() throws Exception {
//Here EDT thread
return calculateData();
}
});
SwingUtilities.invokeLater(task);
try {
return task.get();
} catch (InterruptedException | ExecutionException e) {
logger.log(Level.SEVERE, "Exception", e);
}
答案 2 :(得分:0)
我知道这个问题已经很老了,但是以下内容应该为该问题提供一个方便的解决方法。
您将需要此类来定义一个最终对象,该对象封装您的返回值:
public class Shell<T> {
private T value;
public T getValue() {
return this.value;
}
public void setValue(T value) {
this.value = value;
}
}
随后,您可以运行整个嵌套嵌套Runnable
文件。像这样:
public InfoObj getInformation() {
final Shell<InfoObj> returnObj = new Shell<InfoObj>();
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
returnObj.setValue(mainView.getInfo());
}
});
} catch (InvocationTargetException | InterruptedException e1) {
e1.printStackTrace();
}
return returnVal.getValue();
}
请注意,我从问题中更改了方法名称,因为该方法不是要从Runnable
中调用的。您可以从任何线程调用此方法,它将返回一个值。但是,我也将invokeLater()
更改为invokeAndWait()
,因为如果您在null
中执行的操作未分配a,则可能返回值为Runnable
。值Shell
对象。如果您不希望线程被阻塞并使用invokeLater()
,则还可以返回Shell
对象以在其具有值时读取它。