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

Java 无法在繁重的进程下更新Swing组件,java,multithreading,swing,event-dispatch-thread,invokelater,Java,Multithreading,Swing,Event Dispatch Thread,Invokelater,我在一个匿名SwingWorker线程下运行一个非常繁重的进程。同时,我使用进度条向GUI报告进度。然而,Swing线程正在使我陷入困境。它只是没有及时更新任何东西。我不知道该怎么做,因为我尝试从SwingWorker线程和外部更新GUI,但都拒绝工作 当繁重的工作线程运行时,如何可靠地更新Swing UI? 我尝试过的事情 这不起作用(无论是否在invokeLater命令中包装) 此外,尝试从并发测量线程更新UI不起作用: class LocalCompressor extends Swin

我在一个匿名
SwingWorker
线程下运行一个非常繁重的进程。同时,我使用进度条向GUI报告进度。然而,Swing线程正在使我陷入困境。它只是没有及时更新任何东西。我不知道该怎么做,因为我尝试从
SwingWorker
线程和外部更新GUI,但都拒绝工作

当繁重的工作线程运行时,如何可靠地更新Swing UI? 我尝试过的事情 这不起作用(无论是否在
invokeLater
命令中包装)


此外,尝试从并发测量线程更新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;
        }
        
        // [...]
}
class LocalCompressor扩展SwingWorker{
// [...]
公共压缩机(压缩机){
这个。压缩机=压缩机;
// [...]
}
@凌驾
受保护的Void doInBackground(){
最终线程t1=新线程(新可运行(){
@凌驾
公开募捐{
compressor.compress();
}
});
最终螺纹t2=新螺纹(新可运行(){
@凌驾
公开募捐{
t1.start();
while(t1.isAlive()){
updateUI(compressor.getPercentDone());
}
}
});
t2.start();
返回null;
}
// [...]
}

你并没有真正使用你的SwingWorker。工作线程本身已经是线程。如果您可以将长时间运行的代码放入
doInBackground()
,请将其放在那里。然后用实际进度调用
publish(Integer)
,处理
process(List)
-方法中得到的块。在process()中,您可以更新gui,它位于EDT上

编辑:
实际上,您现在正在做的是在几个while循环中进行轮询,这有点耗电。这就是为什么我认为在你的算法中,每次你得到一个百分比,或者每次循环开始新的一轮或类似的事情时,对你来说更好。

你并不是真的在使用你的SwingWorker。工作线程本身已经是线程。如果您可以将长时间运行的代码放入
doInBackground()
,请将其放在那里。然后用实际进度调用
publish(Integer)
,处理
process(List)
-方法中得到的块。在process()中,您可以更新gui,它位于EDT上

编辑: 实际上,您现在正在做的是在几个while循环中进行轮询,这有点耗电。这就是为什么我认为算法中的事件对你来说更好,每次你得到一个百分比,或者每次循环开始新一轮或者类似的事情。

我假设(你知道怎么回事)对
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();
}
我假设(你知道这是怎么回事)对
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();
}

在这里已经提供的答案和建议的基础上,这里有一种编码方法。我假设压缩器本身没有能力进行回调,但您可以要求它提供完成百分比

在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;
   }
}
public类LocalCompressor扩展SwingWorker
{
私人压缩机;
公共压缩机(压缩机)
{
这个。压缩机=压缩机;
// [...]
}
@凌驾
受保护的void done()
{
System.out.println(“压缩完成了,打算用它做点什么…”);
}
@凌驾
受保护的无效进程(列表块)
{
for(整数百分比:块)
{
progressBar.setValue(百分比);
statusLabel.setText(百分比);
}
}
@凌驾
受保护的Void doInBackground()引发异常
{
最终线程t1=新线程(新可运行()
{
@凌驾
公开募捐
{
compressor.compress();
}
});
t1.start();
while(t1.isAlive())
{
int percentDone=compressor.getPercentDone();
发布(完成百分比);
睡眠(200);
}
返回null;
}
}

在这里提供的答案和建议的基础上,这里有一种编码方法。我假设压缩器本身没有能力进行回调,但您可以要求它提供完成百分比

在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;
   }
}
public类LocalCompressor扩展SwingWorker
{
私人压缩机;
公共压缩机(压缩机)
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);
        }
    }
}
private void compress() {

    // .....

    publish(getPercentDone());

    // .....

}