java - 与jProgressBar同步复制显示

标签 java multithreading synchronization copy jprogressbar

我想监控文件从源复制到目标的进度。我使用了 synchronized 关键字,但不知何故它没有按我预期的方式工作,我的逻辑可能是错误的。如果你能帮助我,我会很高兴。 这是我的代码。

public class Download extends javax.swing.JFrame {
    int val=0;
    private Timer t;
    private ActionListener a;

/* Creates new form Download */
    public Download() {
       initComponents();
       jProgressBar1.setValue(val);
       a = new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            if (jProgressBar1.getValue() < val)
                jProgressBar1.setValue(jProgressBar1.getValue()+1);
            else
                t.stop();
          }        
       };
    }  
    public synchronized void copy(String source,String url)
    {
      try {    
        val+=25;
        t=new Timer(200,a);
        t.start();
        FileInputStream fs = new FileInputStream(source);
        FileOutputStream os = new FileOutputStream(url);
        int b;
        while ((b = fs.read()) != -1) {
           os.write(b);
        }
        os.close();
        fs.close();
      } catch (Exception E) {
        E.printStackTrace();
      }        
    }

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
      JFileChooser chooser = new JFileChooser();
      chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      String url = null;
      int returnValue = chooser.showDialog(null, "Select");
      if (returnValue == JFileChooser.APPROVE_OPTION) {
         url = chooser.getSelectedFile().getPath();
      } else {
         dispose();
      }
      JOptionPane.showMessageDialog(this,"Wait for Completion");  

      if(CB1.isSelected()==true)
      {
        File f = new File(getClass().getResource("/PCycle/Ele.pdf").getFile());
        String source= f.getAbsolutePath(); 
        copy(source,(url+"\\"+CB1.getText()+".pdf"));
      }
      if(CB2.isSelected()==true)
      {
        File f = new File(getClass().getResource("/PCycle/Mech.pdf").getFile());
        String source= f.getAbsolutePath();  
        copy(source,(url+"\\"+CB2.getText()+".pdf"));
      }
      if(CB3.isSelected()==true)
      {
        File f = new File(getClass().getResource("/PCycle/Phy.pdf").getFile());
        String source= f.getAbsolutePath();  
        copy(source,(url+"\\"+CB3.getText()+".pdf"));
      }
      if(CB4.isSelected()==true)
      {
        File f = new File(getClass().getResource("/PCycle/Civil.pdf").getFile());
        String source= f.getAbsolutePath();  
        copy(source,(url+"\\"+CB4.getText()+".pdf"));
      }

      JOptionPane.showMessageDialog(this,"Completed");

      try {
            jProgressBar1.setValue(100);                
            Thread.sleep(3000);
      } catch (InterruptedException ex) {
            Logger.getLogger(Download.class.getName()).log(Level.SEVERE, null, ex);
      }
      System.exit(0);
   }
}

在这里,我尝试实现一种逻辑,每当我们调用“复制”方法时,它都会将文件从一个位置复制到另一个位置,在此之前它应该运行计时器方法,通过该方法来显示 的进度显示jProgressBar。但不幸的是,即使使用synchronized后,它也没有显示每个文件的进度。

最佳答案

问题是您阻塞了 Swing 的事件调度线程 (EDT)。

当 EDT 不忙于响应事件时,Swing 会执行所有绘制操作。在这种情况下,在复制所有文件之前,jButton1ActionPerformed 不会返回。因此,尽管在每次 copy() 调用期间启动了 Timer,但计时器永远没有机会过期,因为 jButton1ActionPerformed 从未返回。

在本例中,您想要使用 SwingWorker在后台线程中复制文件。

  • 当您想要开始复制文件时:
    • 在主线程中启动计时器
    • 创建并启动 SwingWorker
    • 打开模型对话框以阻止进一步的用户操作(或以其他方式禁用 UI)
  • 当计时器到期时,您的进度条将会前进并被绘制。
  • SwingWorker 执行 done() 时(在 EDT 上执行),
    • 停止计时器
    • 关闭对话框(或重新启用用户界面)

注意:不要从后台工作线程创建或访问任何 UI 项目,或者创建/启动/停止计时器。这些操作只能在 EDT 上执行。

<小时/>

粗略示例,显示禁用 UI 元素、启动 SwingWorker、从工作线程发布以显示进度(正在下载哪个文件)、在工作线程完成时启用 UI。

文件复制是使用 3 秒 sleep 伪造的。

package progress;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class Download extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Download::new);
    }

    private final JButton downloadBtn = new JButton("Start Download");
    private final JProgressBar progressBar = new JProgressBar();
    private final Timer timer = new Timer(200, this::timerTick);

    Download() {
        super("Download Example");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 300);
        setLocationByPlatform(true);

        downloadBtn.addActionListener(this::startDownload);
        add(downloadBtn, BorderLayout.PAGE_START);

        progressBar.setStringPainted(true);
        add(progressBar, BorderLayout.PAGE_END);

        setVisible(true);
    }

    private void startDownload(ActionEvent evt) {
        downloadBtn.setEnabled(false);
        timer.start();
        DownloadWorker worker = new DownloadWorker("File1", "FileB", "AnotherFile");
        worker.execute();
    }

    private void timerTick(ActionEvent evt) {
        progressBar.setValue(progressBar.getValue()+2);
    }

    private class DownloadWorker extends SwingWorker<Void, String> {

        private final String[] files;

        DownloadWorker(String ...files) {
            this.files = files;

            progressBar.setValue(0);
        }

        @Override
        protected Void doInBackground() throws Exception {
            for(String file : files) {
                publish(file);

                // Copy the file
                Thread.sleep(3000);  // Pretend copy takes a few seconds
            }
            return null;
        }

        @Override
        protected void process(List<String> chunks) {
            String file = chunks.get(chunks.size()-1);  // Just last published filename
            progressBar.setString("Downloading "+file + " ...");
        }

        @Override
        protected void done() {
            progressBar.setString("Complete");
            progressBar.setValue(100);
            timer.stop();
            downloadBtn.setEnabled(true);   // Re-enable UI
        }
    }
}

关于java - 与jProgressBar同步复制显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45593447/

相关文章:

java - 在 javacv 中将 IplImage 转换为 Mat

c - 可靠地将相同的数据广播到 C 中的多个套接字

android - Activity的后台线程和配置更改

ios - 在NSURLSession之后同步执行方法

java - 启动 Liferay 7 (liferay-portal-7.0-ce-ga1) 时出错 : PWC6345: There is an error in invoking javac. 需要完整的 JDK(不仅仅是 JRE)

java - JPA:非唯一 "OneToMany"集合

java - 尝试使Java中的音频失真

c - 我是线程和信号量的新手,我似乎可以理解为什么我的代码无法正常工作,我们将不胜感激

java - 迭代 LinkedHashSet 时跳过同步并且未完成删除?

FFmpeg concat 然后将音轨结果添加到口吃的音轨中