1

Я хочу информировать своего пользователя о ходе операции ввода-вывода. На данный момент у меня есть внутренний класс, который я начинаю, прежде чем начинать свой ввод-вывод и останавливаться после его завершения. Это выглядит следующим образом:Обновление прогона с использованием процессора

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%, когда останавливается, потому что иногда ввод-вывод заканчивается и останавливает мой обновитель, прежде чем он сможет завершить обновление предыдущего приращения. Это просто гарантирует, что он на 100%.

На данный момент, это работает, и я использую его так:

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

Проблема заключается в том, что петля ест процессор довольно плохо. Я пытался бросить блокировку (с ожиданием/уведомлением), но я не знаю, что я делаю, когда дело доходит до использования wait/notify, поэтому оно просто зависает. Что я могу сделать, чтобы остановить его от использования так много циклов процессора?

+0

Что-то кажется мне неправильным. Его цикл, даже если нет прогресса. Theres no вид ожидания или условия для повторного цикла. Он будет целенаправленно проверять 'if (position> last)' миллион раз, даже если все время он терпит неудачу. В этой вещи должен быть какой-то «сон»(). – Havenard

+1

Вам не нужно сразу обновлять информацию, вы можете обновлять каждые 100 мс, и пользователю он не будет отличаться от обновления в реальном времени. – Havenard

+0

@ Havenard Я закончил с вашим предложением, я просто добавил сон там, и теперь он работает как сон. Если вы хотите ответить на этот старый вопрос, о котором я забыл, я соглашусь. – Logan

ответ

6

Вы должны изучить использование SwingWorker. В основном вы делаете все IO в фоновом потоке, который предоставляет SwingWorker, и есть встроенные способы сообщения о прогрессе в качающийся поток.

Затем, когда вы обновляете эту позицию, вы можете автоматически обновлять ход, а не опрос непрерывно, что вы сделали до сих пор.

Посмотрите на Swingworker Timeout или SwingWorker with FileReader, чтобы узнать, поможет ли это.

Альтернативное решение, если вы не хотите использовать SwingWorker, вероятно, просто быть, вместо того, чтобы обновить это значение position, обновлять индикатор непосредственно, с вызовом, как это:

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

Предполагая, что вы настроили индикатор выполнения так, чтобы его макс был размером с файл.

+0

My IO уже на фоновом потоке, не имеет значения? – Logan

+0

Я бы сказал, что «SwingWorker» - это «нормальный» способ сделать то, что вы сделали, если приложение - это в первую очередь приложение Swing. Вы можете определенно сделать это с помощью любого другого фонового приложения, которое вам нравится. Я сделаю небольшое редактирование здесь другим способом. –

+1

@LoganDam Нет, но было бы проще повторно синхронизировать обратные вызовы с пользовательским интерфейсом, так как вы НИКОГДА, НИКОГДА не создавайте или не изменяйте какой-либо компонент пользовательского интерфейса из любого потока, кроме EDT. Преобразование IO для использования SwingWorker действительно тривиально. Проверьте [это] (http://stackoverflow.com/questions/15668715/how-to-use-the-swing-timer-to-delay-the-loading-of-a-progress-bar/15669717#15669717) пример для подробного примера – MadProgrammer

5

Прежде всего никогда не используйте Thread для обновления GUI (компоненты Swing) в java. Вместо этого используйте javax.swing.Timer или javax.swing.SwingWorker. А для работы с основным вводом/выводом используется ProgressMonitorInputStream.
Как простой пример чтения файла и отображения его в JTextArea и progrressbar..Have посмотреть на код, указанный ниже:

enter image description here

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(); 
      } 
     }); 
    } 
}