【问题标题】:progressbar in a thread does not update its UI until the work was done in the main thread在主线程中完成工作之前,线程中的进度条不会更新其 UI
【发布时间】:2011-10-24 10:59:43
【问题描述】:

我正在将一个大文件切割成块,并希望显示进度。 当我点击 startCut 按钮时,执行代码如下:

FileInputStream in = new FileInputStream(sourceFile);
int blockSize = (int)(getSelectedBlockSize() * 1024);
int totalBlock = Integer.parseInt(txtNumberOfBlock.getText());
byte[] buffer = new byte[blockSize];
int readBytes = in.read(buffer);
int fileIndex = 1;

class PBThread extends Thread
{
    @Override
    public void run()
    {
        while(true)
        {
            pbCompleteness.setValue(value);
            //value++; //place A
            System.out.println(value);
            if (value >= 100)
                break;
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
        }
    }
}
value = 0;
PBThread pbThread = new PBThread();
pbThread.start();
while(readBytes != -1)
{
    File file = new File(targetFilePath + fileIndex);
    FileOutputStream out = new FileOutputStream(file);
    out.write(buffer, 0, readBytes);
    out.close();
    value = (int)(fileIndex / (double)totalBlock * 100);// place B
    readBytes = in.read(buffer);
    fileIndex++;
}

我在B处的run方法之外改变了progressbar的值,问题是——grogressbar只显示两种状态:0%和100%。 但是,如果我拿走 B 处的代码,并在 A 处的 run 方法中更改进度条的值,问题就会消失。 我知道也许SwingWorker 可以很容易地修复它,但我确实想知道为什么会这样,虽然我在运行方法中更改了值,但当我在运行方法中打印出来时,它确实改变了。 如何在 run 方法之外更改值时解决该问题?

【问题讨论】:

  • 除了您已经收到的好建议之外.. Thread.sleep(100); - 不要那样做。如果 GUI 中需要延迟(例如动画),请创建一个 Swing Timer

标签: java swing progress-bar


【解决方案1】:

问题的症结在于您正在更新组件:pbCompleteness 在线程上不是事件调度线程。您可以在 run() 方法中使用 SwingUtilities.invokeLater 解决此问题;例如

AtomicInteger value = new AtomicInteger(0);
while (true) {
  SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      pbCompleteness.setValue(value.get());
    }
  });

  // Do some work and update value.
}

这将导致 JProgressBar 在您的工作线程继续运行时在 Event Dispatch 线程上更新(并重新绘制)。请注意,为了在“内部”匿名Runnable 实例中引用value,我已将其更改为AtomicInteger。这也是可取的,因为它使它成为线程安全的。

【讨论】:

  • 刚刚学到了一些关于Event Dispatch Thread的知识,明白你说的了,谢谢
  • 问题不在于他正在从 EDT 更新 Swing 组件——这会导致 NPE 或可能出现竞争条件。由于他的 Swing 被锁定,他在 EDT on 上调用了一段长时间运行的代码,可能是这部分:while(readBytes != -1) {...},它需要在后台线程中。
  • 我试过你的方法,还是有问题,如何用你的方法给你看我的新代码?
  • “我如何向您展示我的新代码” 编辑问题(然后在每个人的评论中通知回复者 - 让他们知道)。
【解决方案2】:

这里有两个问题:

  • 您正在 Swing 调度程序线程中执行长时间运行的工作,这意味着您正在阻止它处理事件。 (尝试移动窗口等 - 它会失败。)
  • 您在 A 点从错误的线程更新 UI。听起来您现在已经摆脱了这个问题,但这仍然是一个错误。

您应该使用SwingWorkerSwingUtilities 来解决这两个问题。基本上,您不能从非 UI 线程访问 UI,也不能在 UI 线程上执行长时间运行的工作。请参阅Swing concurrency tutorial 了解更多信息。

【讨论】:

  • 我会记住你说的话。你帮了我很多。非常感谢
【解决方案3】:

我找到了一个非常简单的方法来解决这个问题:D。您可以使用此代码并非常简单地处理此错误:)。

int progress=0;
Random random = new Random();
while (progress < 100) {
 //Sleep for up to one second.
 try {
       Thread.sleep(random.nextInt(100));
} catch (InterruptedException ignore) {}
progress+=1;
progressBar.setValue(progress);
Rectangle progressRect = progressBar.getBounds();//important line 
progressRect.x = 0;
progressRect.y = 0;         
progressBar.paintImmediately(progressRect);//important line 
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-31
    • 1970-01-01
    • 2015-09-03
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    相关资源
    最近更新 更多