无法在繁重的流程下更新Swing组件

时间:2012-10-03 19:26:35

标签: java multithreading swing event-dispatch-thread invokelater

我在匿名SwingWorker线程下运行一个非常繁重的进程。与此同时,我正在使用进度条报告GUI的进度。但是,Swing线程正在帮助我。它根本不会及时更新任何内容。我不知道该怎么做,因为我尝试从SwingWorker线程和外部更新GUI,并且都拒绝工作。

如何在繁重的工作线程运行时可靠地更新Swing UI?

我尝试过的事情

这不起作用(包含或不包含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;
        }

        // [...]
}

5 个答案:

答案 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已经是一个自己的线程。因此,删除内部线程(t1t2)并在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值进行游戏,看看它是否有任何区别