我在匿名SwingWorker
线程下运行一个非常繁重的进程。与此同时,我正在使用进度条报告GUI的进度。但是,Swing线程正在帮助我。它根本不会及时更新任何内容。我不知道该怎么做,因为我尝试从SwingWorker
线程和外部更新GUI,并且都拒绝工作。
这不起作用(包含或不包含invokeLater
命令)。
new LocalCompressor(compressor).execute();
while (!compressionDone) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
});
}
此外,尝试从并发测量线程更新UI不起作用:
class LocalCompressor extends SwingWorker<Void, Void> {
// [...]
public LocalCompressor(Compressor compressor) {
this.compressor = compressor;
// [...]
}
@Override
protected Void doInBackground() {
final Thread t1 = new Thread(new Runnable() {
@Override
public void run(){
compressor.compress();
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
t1.start();
while (t1.isAlive()) {
updateUI(compressor.getPercentDone());
}
}
});
t2.start();
return null;
}
// [...]
}
答案 0 :(得分:2)
你并没有真正使用你的SwingWorker。工人已经是自己的线程。如果您可以将长时间运行的代码放入doInBackground()
,请将其放在那里。然后使用您的实际进度调用publish(Integer)
并处理您在process(List<Integer>)
- 方法中获得的块。在过程中()你可以更新gui,它在EDT上。
编辑: 实际上,你现在正在做的是在几个循环中进行轮询,这有点耗费精力。这就是为什么我认为你的算法中的事件更好,每次你得到一个百分比或每次循环开始新一轮或类似的东西。
答案 1 :(得分:1)
扩展此处提供的答案和建议,以下是一种编码方法。我假设压缩器本身没有能力进行回调,但你可以问它完成的百分比。
在swingworker线程(doInBackground)中,我们启动真正的压缩线程。然后在后台线程中启动一个轮询循环,每秒更新一次UI。要通知UI线程,请调用publish。这将导致在事件线程中定期调用重写的方法进程。从这里我们可以安全地更新进度条和状态标签。
public class LocalCompressor extends SwingWorker<Void, Integer>
{
private Compressor compressor;
public LocalCompressor(Compressor compressor)
{
this.compressor = compressor;
// [...]
}
@Override
protected void done()
{
System.out.println("Compression is done. Going to do something with it...");
}
@Override
protected void process(List<Integer> chunks)
{
for (Integer percent : chunks)
{
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
@Override
protected Void doInBackground() throws Exception
{
final Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
compressor.compress();
}
});
t1.start();
while (t1.isAlive())
{
int percentDone = compressor.getPercentDone();
publish(percentDone);
Thread.sleep(200);
}
return null;
}
}
答案 2 :(得分:1)
您是否尝试过使用SwingWorker
的简单基本方法?就像之前所说的@Zhedar一样,SwingWorker已经是一个自己的线程。因此,删除内部线程(t1
,t2
)并在compress()
中使用耗时的doInBackground()
方法。
Something 非常基本,如下所示:
class LocalCompressor extends SwingWorker<Void, Integer> {
// .....
// Your constructor here
// .....
@Override
protected Void doInBackground() throws Exception {
compress();
return null;
}
@Override
protected void process(List<Integer> chunks) {
for (Integer chunk : chunks) {
progressBar.setValue(chunk);
statusLabel.setText(chunk);
}
}
}
现在,这个compress()
方法应该移到SwingWorker
内,并且它必须位于某个publish()
,在您的情况下可能是publish(getPercentDone())
或其他任何方式。
private void compress() {
// .....
publish(getPercentDone());
// .....
}
通常使用SwingWorker
来完成这项工作。
答案 3 :(得分:0)
我假设(你知道怎么回事)对LocalCompressor.execute()
的调用是阻塞的。如果是这种情况,你的while循环将不会运行,直到完成所有操作,然后你就失去了在你的UI上获得稳定的更新流的目的。
给这个或类似的东西一个镜头:
LocalCompressor comp = new LocalCompressor(compressor);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
while (!compressionDone) {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
});
comp.execute();
}
答案 4 :(得分:0)
您可以雇用生产者/消费者模式......
这是一个非常基本的概念......
public class ProducerComsumer {
public static void main(String[] args) {
new ProducerComsumer();
}
public ProducerComsumer() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(new EmptyBorder(12, 12, 12, 12));
JProgressBar progressBar = new JProgressBar();
panel.add(progressBar);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Producer producer = new Producer();
producer.start();
Consumer consumer = new Consumer(producer, progressBar);
consumer.start();
}
});
}
public class Producer extends Thread {
private volatile float progress;
private volatile boolean done;
public Producer() {
setPriority(NORM_PRIORITY - 1);
setDaemon(true);
}
public float getProgress() {
return progress;
}
public boolean isDone() {
return done;
}
@Override
public void run() {
done = false;
for (int index = 0; index < Integer.MAX_VALUE; index++) {
progress = (float) index / (float) Integer.MAX_VALUE;
}
done = true;
System.out.println("All done...");
}
}
public class Consumer extends Thread {
private Producer producer;
private JProgressBar progressBar;
public Consumer(Producer producer, JProgressBar progressBar) {
setDaemon(true);
setPriority(NORM_PRIORITY - 1);
this.producer = producer;
this.progressBar = progressBar;
}
public JProgressBar getProgressBar() {
return progressBar;
}
public Producer getProducer() {
return producer;
}
@Override
public void run() {
while (!producer.isDone()) {
updateProgress();
try {
sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(ProducerComsumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
updateProgress();
}
protected void updateProgress() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
int progress = Math.round(getProducer().getProgress() * 100f);
System.out.println("Update progress to " + progress);
getProgressBar().setValue(progress);
}
});
}
}
}
使用Thread.setPriority
值进行游戏,看看它是否有任何区别