问题描述
EDT 如何与正在执行的 SwingWorker
通信? SwingWorker
有很多方法可以将信息传送回 EDT - 例如发布/处理和属性更改,但没有定义的方式(我见过)在另一个方向进行通信。似乎很好的旧 Java 并发线程间通信将是通过 wait()
和 notify()
的方式。这不起作用。我稍后会解释。我实际上让它工作了,但它使用了一个丑陋的轮询机制。我觉得应该有更好的方法。这是我试图完成的过程:
- 用户从主
Swing
UI (EDT) 启动SwingWorker
长时间运行的任务(引擎)。 - 在某些时候,引擎需要来自 EDT 的信息,以便将这些信息反馈给 EDT。这可以通过可见 UI 组件的发布/处理更新来完成。重要的是,这一步不会阻止 EDT,因为其他事情也在发生。
- 引擎阻塞等待答案。
- 在某些时候,用户会注意到视觉指示,并通过某些 UI (EDT) 功能(例如按下
Swing
按钮)提供所需信息。 - EDT 更新引擎上的对象。然后“唤醒”引擎。
- 引擎引用更新后的对象并继续处理。
我对 wait()
/notify()
的问题是,在第 3 步中,在 wait()
中对 doInBackground()
的任何调用都会导致 done()
方法立即被触发和要终止的 SwingWorker
。
通过在 sleep()
中使用丑陋的 doInBackground()
循环,我能够使上述过程起作用:
for (;;)
{
Thread.sleep(10);
if (fromEDT != null)
{
// Process the update from the EDT
System.out.println("From EDT: " + fromEDT);
fromEDT = null;
break;
}
}
在第 5 步中,引擎会自行唤醒并检查来自 EDT 的更新。
解决方法
以下是一个演示 SwingWorker
暂停并等待用户输入的代码:
import java.awt.*;
import java.util.List;
import javax.swing.*;
public class SwingWorkerWaitDemo {
public static void creategui(){
JFrame f = new JFrame("SwingWorker wait Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.add(new MainPanel());
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
creategui();
}
}
class MainPanel extends JPanel {
private static final String BLANK = " ";
private MyWorker swingWorker;
private final JLabel output,msg;
private final JButton start,stop,respond;
MainPanel() {
setLayout(new BorderLayout(2,2));
start = new JButton("Start");
start.addActionListener(e->start());
stop = new JButton("Stop");
stop.setEnabled(false);
stop.addActionListener(e->stop());
JPanel ssPane = new JPanel(new FlowLayout(FlowLayout.CENTER));
ssPane.add(start); ssPane.add(stop);
add(ssPane,BorderLayout.PAGE_START);
output = new JLabel(BLANK);
JPanel outputPane = new JPanel(new FlowLayout(FlowLayout.CENTER));
outputPane.add(output);
add(outputPane,BorderLayout.CENTER);
msg = new JLabel(BLANK);
respond = new JButton("Respond");
respond.addActionListener(e->respond());
respond.setEnabled(false);
JPanel responsePane = new JPanel();
responsePane.add(msg); responsePane.add(respond);
add(responsePane,BorderLayout.PAGE_END);
}
@Override
public Dimension getPreferredSize(){
return new Dimension(400,200);
}
private void start() {
start.setEnabled(false);
stop.setEnabled(true);
swingWorker = new MyWorker();
swingWorker.execute();
}
private void stop() {
stop.setEnabled(false);
swingWorker.setStop(true);
}
private void message(String s){
msg.setText(s);
}
private void clearMessage(){
msg.setText(BLANK);
}
private void askForUserResponse(){
respond.setEnabled(true);
message("Please respond " );
}
private void respond(){
clearMessage();
respond.setEnabled(false);
swingWorker.setPause(false);
}
class MyWorker extends SwingWorker<Integer,Integer> {
private boolean stop = false;
private volatile boolean pause = false;
@Override
protected Integer doInBackground() throws Exception {
int counter = 0;
while(! stop){
publish(counter++);
if(counter%10 == 0) {
pause = true;
askForUserResponse();
while(pause){ /*wait*/ }
}
Thread.sleep(500);
}
return counter;
}
@Override
protected void process(List<Integer> chunks) {
for (int i : chunks) {
output.setText(String.valueOf(i));
}
}
@Override
protected void done() {
message("All done");
}
void setStop(boolean stop) {
this.stop = stop;
}
void setPause(boolean pause) {
this.pause = pause;
}
}
}