在主线程中完成工作之前,线程中的进度条不会更新其UI

我正在将一个大文件切成块,并希望显示进度的速度。 当我单击startCut Button时,这是要执行的代码:

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方法之外的进度条的值,问题是 – grogressbar只显示两个状态:0%和100%。 但是,如果我把代码拿走到位B,并在位置A的run方法中更改进度条的值,问题就会消失。 我知道也许使用SwingWorker它可以轻松修复,但我确实想知道为什么会发生这种情况,虽然我改变了run方法的值,当我在run方法中打印出来时,它确实发生了变化。 如何在运行方法之外更改值时修复该问题?

问题的关键在于您在事件调度线程以外的线程上更新组件: 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实例中的值,我将其更改为AtomicInteger 。 这也是可取的,因为它使它具有线程安全性。

你有两个问题:

  • 你正在Swing调度程序线程中进行长时间运行,这意味着你要阻止它处理事件。 (尝试移动窗户等 – 它会失败。)
  • 你正在A点从错误的线程更新UI。听起来你现在正在逃避这一点,但它仍然是一个bug。

您应该使用SwingWorkerSwingUtilities来解决这两个问题。 基本上,您不能从非UI线程访问UI,并且您不能在UI线程上进行长时间运行的工作。 有关更多信息,请参阅Swing并发教程 。

我找到了一个非常简单的方法来解决这个问题:D。 你可以使用这个代码,非常simlpe handel这个错误:)。

 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 }