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 がどこまで進んだかを表す外部クラスのフィールドです。以前のインクリメントの更新を完了する前に、I/O が終了してアップデーターを停止することがあるため、停止するときに進行状況を 100% に設定します。これだけで 100% になることが保証されます。

現時点では、これは機能し、次のように使用します。

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

問題は、ループがかなりひどく CPU を消費することです。そこに(待機/通知を使用して)ロックを投げてみましたが、待機/通知の使用に関しては何をしているのかわからないため、スレッドがハングしました。非常に多くの CPU サイクルを使用しないようにするにはどうすればよいですか?

4

2 に答える 2

6

SwingWorkerの使用を検討する必要があります。基本的に、 が提供するバックグラウンド スレッドですべての IO を実行します。swing スレッドSwingWorkerに進行状況を報告する方法が組み込まれています。

その後、その位置を更新するたびに、これまで行ってきたように継続的にポーリングするのではなく、進行状況を自動的に更新できます。

Swingworker TimeoutまたはSwingWorker with FileReaderを見て、どちらかが役立つかどうかを確認してください。

を使用したくない場合の別の解決策は、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.Timerまたはを使用します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 に答える