进度条更新程序使用CPU

lda*_*dam 1 java swing multithreading jprogressbar event-dispatch-thread

我想让用户了解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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

ProgressUpdater updater = new ProgressUpdater(file.length());
updater.start();
//do I/O
//...
updater.stop();
Run Code Online (Sandbox Code Playgroud)

问题是循环吃CPU非常糟糕.我尝试在那里抛出一个锁(带有等待/通知)但我不知道在使用wait/notify时我正在做什么所以它只是挂了我的线程.我该怎么做才能阻止它使用如此多的CPU周期?

Rob*_*b I 6

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

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

看看的SwingWorker超时SwingWorker的用的FileReader,看看要么会有所帮助.

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

SwingUtilities.invokeLater(new Runnable() {
    @Override public void run() {
        getMyProgressBar().setValue(position);
    }
});
Run Code Online (Sandbox Code Playgroud)

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

  • @LoganDam如果没有看到你正在使用的代码(对于SwingWorker),很难指出机会的区域.但是如果你使用发布,这往往会将进程调用合并到一个列表中,这应该会有所帮助 (2认同)

Vis*_*l K 5

首先,永远不要Thread用来更新java中的GUI(Swing组件).而是使用javax.swing.Timerjavax.swing.SwingWorker.对于Basic 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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)