1

我想让我的用户了解 I/O 操作的进度。目前我有一个内部类,我在开始 I/O 之前启动它并在它完成后停止。它看起来像这样:

class ProgressUpdater implements Runnable {

        private Thread thread;
        private long last = 0;
        private boolean update = true;
        private long size;

        public ProgressUpdater(long size) {
            this.size = size;
            thread = new Thread(this);
        }

        @Override
        public void run() {
            while (update) {
                if (position > last) {
                    last = position;
                    double progress = (double) position / (double) size * 100d;
                    parent.setProgress((int) progress);
                }
            }
        }

        public void start() {
            thread.start();
        }

        public void stop() {
            update = false;
            parent.setProgress(100);
        }
    }

parent是我对我的 UI 的引用,position是我外部类中的一个字段,表示我们在 I/O 中取得的进展。停止时我将进度设置为 100%,因为有时 I/O 完成并停止我的更新程序,然后才能完成对先前增量的更新。这只是确保它是 100%。

目前,这有效,我像这样使用它:

ProgressUpdater updater = new ProgressUpdater(file.length());
updater.start();
//do I/O
//...
updater.stop();

问题是循环非常严重地消耗 CPU。我尝试在那里扔一个锁(带有等待/通知),但我不知道在使用等待/通知时我在做什么,所以它只是挂起了我的线程。我能做些什么来阻止它使用这么多 CPU 周期?

4

2 回答 2

6

您应该考虑使用SwingWorker。基本上,您在SwingWorker提供的后台线程上执行所有 IO,并且有内置的方法将进度报告给 swing 线程。

然后,每当您更新该位置时,您都可以自动更新进度,而不是连续轮询,这是您迄今为止所做的。

看看Swingworker TimeoutSwingWorker with FileReader看看是否有帮助。

另一种解决方案,如果您不想使用 a SwingWorker,可能只是position直接更新进度条,而不是更新该值,调用如下:

SwingUtilities.invokeLater(new Runnable() {
    @Override public void run() {
        getMyProgressBar().setValue(position);
    }
});

假设您已设置进度条,使其最大值为文件大小。

于 2013-03-29T20:23:51.910 回答
5

首先,永远不要Thread在 java 中使用更新 GUI(Swing 组件)。而是使用javax.swing.Timeror javax.swing.SwingWorker。对于基本 I/O 操作,请使用 ProgressMonitorInputStream
作为读取文件并将其显示在 JTextArea 和 progrressbar 中的简单示例。看看下面给出的代码:

在此处输入图像描述

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.io.*;
import java.beans.*;
class ProgressBarFrame extends JFrame 
{
    JProgressBar progressBar;
    int BUFFERSIZE = 10;
    JTextArea textArea;
    MyWorker worker;
    private void createAndShowGUI()
    {
        progressBar = new JProgressBar(0,100);
        progressBar.setStringPainted(true);
        JButton button = new JButton("Read File");
        textArea = new JTextArea(30,100);
        JScrollPane jsp = new JScrollPane(textArea);
        Container c = getContentPane();
        c.add(jsp);
        c.add(progressBar,BorderLayout.NORTH);
        c.add(button,BorderLayout.SOUTH);
        worker = new MyWorker();
        button.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent evt)
            {
                textArea.setText("");
                progressBar.setValue(0);
                if (worker.getState()== SwingWorker.StateValue.DONE || worker.getState()==SwingWorker.StateValue.STARTED)
                {
                    worker.cancel(true);
                    worker = new MyWorker();
                }
                worker.execute();

            }
        });
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }
    class MyWorker extends SwingWorker<Void, String> 
    {
        MyWorker()
        {
            addPropertyChangeListener(new PropertyChangeListener() 
            {
                public  void propertyChange(PropertyChangeEvent evt) 
                {
                    if ("progress".equals(evt.getPropertyName())) 
                    {
                        progressBar.setValue((Integer)evt.getNewValue());
                    }
                }
            });
        }

        @Override
        public Void doInBackground() throws IOException
        {
            File file = new File("ProgressBarFrame.java");
            long size = file.length();
            long temp = 0;
            BufferedInputStream bfin = new BufferedInputStream(new FileInputStream(file));
            byte[] buffer=new byte[BUFFERSIZE];
            int totalRead = -1;
            while ((totalRead=bfin.read(buffer))!=-1 && ! isCancelled()) 
            {
                temp = temp + totalRead;
                publish(new String(buffer));
                if (bfin.available()<BUFFERSIZE && bfin.available()!= 0)
                buffer = new byte[bfin.available()];
                else if(bfin.available()==0)
                buffer = new byte[1];
                else
                buffer=new byte[BUFFERSIZE];
                setProgress((int)((temp/(float)size) * 100));
                try{Thread.sleep(1);}catch(Exception ex){}
            }
            setProgress(100);
            return null;
        }
        @Override
        protected void process(List<String> chunks) 
        {
            for (String value : chunks) 
            {
                textArea.append(value);
            }
        }
    }
    public static void main(String st[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                ProgressBarFrame pf = new ProgressBarFrame();
                pf.createAndShowGUI();
            }
        });
    }
}
于 2013-03-29T20:26:55.887 回答