我对如何正确安全地将数据从事件调度线程(EDT)上的GUI元素传递到需要与该数据交互的SwingWorker感到困惑。我已经关注了stackoverflow和网络上的许多例子;无论我尝试编写哪种方式,似乎我打破了誓言,从不接触来自不同线程的GUI元素。
我试图说明我对伪代码的困惑。
我有一个带有一些swing元素的JFrame来接受用户的输入。用户单击JButton,JButton禁用,因此用户无法单击两次,并且他们的输入将在后台线程中传递给SwingWorker:
import javax.swing.*;
import java.awt.event.*;
class GUI implements Runnable{
JFrame jFrame;
JTextField userInputTextField;
JButton beginLongTaskButton;
GUI(){
//initialize the GUI elements, add them to JFrame, JPanel, etc.
this.beginLongTaskButton = new JButton("Begin Task");
this.beginLongTaskButton.addActionListener(a -> beginLongTask());//lambda sure is nice
}
void beginLongTask(){
this.beginLongTaskButton.setEnabled(false);
this.beginLongTaskButton.setText("Doing your long task...");
LongTaskWorker longTaskWorker = new LongTaskWorker(this.userInputTextField);//isn't this bad?
longTask.execute();
}
@Override
public void run(){
this.jFrame.setVisible(true);//blah blah
}
}
此时,EDT应该只是坐得漂亮,除了一个问题:SwingWorker通过其构造函数获得了一些GUI元素:
import javax.swing.*;
class LongTaskWorker extends SwingWorker<Void, Void>{
JTextField userInputTextField;
public LongTaskWorker(final JTextField userInputTextField){
this.userInputTextField = userInputTextField;//the text field on EDT I'm not supposed to touch
}
@Override
protected Void doInBackground() throws Exception{
//read lots of stuff from JTextField in this Thread (the wrong thread)?
//write? lots of stuff to JTextField in this Thread (the wrong thread)?
//do lots of other stuff to JTextField?
return null;
}
}
我见过许多人做类似的事情。我认为即使是JavaDoc SwingWorker示例也是这样做的。但这并不意味着当我不应该在不同的线程上弄乱对象时?读取(不更改)GUI组件的值是否仍然违反了跨线程规则?如果我的SwingWorker必须经常从GUI元素中读取数据,那会不会减慢EDT的速度?
也许正确的做法是从EDT中的GUI元素中提取我需要的数据,然后将提取的数据传递给SwingWorker?而不是直接将GUI元素传递给SwingWorker。
答案 0 :(得分:1)
我认为你真正想要的是你的视图模型。那就是:名称建议的视图是您的GUI及其元素 - JTextFields。您的模型将是81值的支持地图。现在,如果您想在其上计算某些内容,您可以将支持Map传递给该方法(SwingWorker)。如果某些内容更改了支持模型,则事件应触发GUI刷新(从模型加载值)。如果用户更改GUI中的值,则应通过控制器进行更改。它反过来改变模型并启动Change-Event。
这样你也可以避免反馈循环。
你可以阅读这个概念,寻找&#34; MVC&#34;或&#34;模型视图控制器&#34;。