【问题标题】:How to synchronize two threads so they both get done at the same time in Java如何同步两个线程,以便它们在 Java 中同时完成
【发布时间】:2017-03-05 12:34:36
【问题描述】:

我正在尝试实现一个进度条,用于确定 Java 中另一个线程的(近似)完成百分比。现在我有两个线程同时运行,但我不确定如何链接到另一个线程。我需要它从 0 开始,当另一个朋友完成时,我希望进度条显示 100。这就是我到目前为止所拥有的。我几乎整天都在寻找解决方案,但似乎找不到任何东西。有什么建议吗?

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileSystemView;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.File;

public class ProgressBarTest extends JFrame {
    JButton browse, search;
    JTextField directory;
    JProgressBar progressBar;
    File file;
    JList<File> results;
    JScrollPane scrollPane;

    // Launch the app
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    new ProgressBarTest();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    // Create the app
    private ProgressBarTest() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("We are testing the JProgressBar");

        browse = new JButton("Browse");
        browse.setActionCommand("browse");
        browse.addActionListener(new ButtonListener());

        search = new JButton("Search");
        search.setActionCommand("search");
        search.addActionListener(new ButtonListener());

        directory = new JTextField(20);
        directory.setEnabled(false);

        file = new File(System.getProperty("user.home"));
        results = new JList<File>(file.listFiles());
        results.setCellRenderer(new MyCellRenderer());
        results.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        results.setVisibleRowCount(-1);

        scrollPane = new JScrollPane(results, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setPreferredSize(new Dimension(408, 100));

        progressBar = new JProgressBar(0,100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);

        JPanel panel1 = new JPanel();
        panel1.add(browse);
        panel1.add(directory);
        panel1.add(search);

        JPanel panel2 = new JPanel();
        panel2.add(scrollPane);

        JPanel panel3 = new JPanel();
        panel3.add(progressBar);

        this.add(panel1, BorderLayout.PAGE_START);
        this.add(panel2, BorderLayout.CENTER);
        this.add(panel3, BorderLayout.PAGE_END);

        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    private class MyCellRenderer extends DefaultListCellRenderer {
        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            JLabel l = (JLabel)c;
            File f = (File)value;
            l.setText(f.getName());
            l.setPreferredSize(new Dimension(130, 20));
            l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
            l.setBorder(new EmptyBorder(3,3,3,3));

            return l;
        }
    }

    private class ButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            if(event.getActionCommand().equals("browse")) {
                JFileChooser chooser = new JFileChooser(System.getProperty("user.home"));
                chooser.setDialogTitle("Search what?");
                chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                chooser.showOpenDialog(null);

                directory.setText(chooser.getSelectedFile().getPath());
            } else if (event.getActionCommand().equals("search")) {
                FileThread ft = new FileThread();
                ft.start();

                ProgressBarThread pbt = new ProgressBarThread();
                pbt.start();

            }
        }
    }

    private class FileThread extends Thread {
        public void run() {
            file = new File(directory.getText().trim());
            results.setListData(file.listFiles());
        }
    }

    private class ProgressBarThread extends Thread {
        public void run() {
            for (int i = 0; i <= 100; i++) {
                progressBar.setValue(i);
                progressBar.setString(Integer.toString(i));
                try {
                    Thread.sleep(50);
                } catch(InterruptedException err) {
                    err.printStackTrace();
                }
            }
        }
    }
}

我知道我必须用决定第一个线程花费的百分比/时间的东西来取出 for 循环。只是不确定这是如何完成的。

【问题讨论】:

  • 链接线程?我认为你对你需要做什么感到困惑。您不想链接线程,而是简单地将进程从在后台线程上运行的一段代码传递到在 Swing 事件线程上运行的另一段代码。
  • 您的代码还从后台线程进行 Swing 突变更改,这是您永远不应该做的事情。请阅读 Swing 线程教程以了解如何正确处理此问题:Lesson: Concurrency in Swing
  • @HovercraftFullOfEels 我是 Java 语言和线程的新手。我只是说一下我在 youtube 视频和一些示例上看到的内容。
  • 如果您的目标是在搜索目录时使进度条不断前进,那么您可能不走运。为此,在后台运行的任务必须能够发送您的程序通知并量化其进度,我看不到您的文件搜索能够做到这一点。这种情况通常需要显示一个不确定的 JProgressBar。
  • @HovercraftFullOfEels 那是我的目标。在我的真实项目(非测试应用程序)中,我让它返回所有子文件夹和文件,它们可能有数百个文件并且需要一段时间才能显示它们。

标签: java multithreading jprogressbar


【解决方案1】:

关键是

  1. 确保您尝试将所有长时间运行的处理卸载到后台线程
  2. 避免在 Swing 事件线程以外的任何线程上进行 Swing 调用,尤其是突变的 Swing 调用。
  3. 如果您无法量化需要完成的工作量并且无法通过后台任务通知已完成该工作的百分比,请考虑将 JProgressBar 设置为不确定。
  4. 否则使用确定的进度条,但让您的后台任务以间接方式通知 Swing GUI 进度量,这通常通过带有回调的侦听器(例如 PropertyChangeListener)来完成。

例如,我尝试使用以下代码执行上述操作,该代码递归地列出目录和子目录中的文件,但我还没有 100% 满意。它使用所选目录的主要子目录的完成,但它以某种方式冻结了 GUI,这意味着我仍然在事件线程上调用了 cpu 或占用时间的代码。这里需要做更多的工作......

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileFilter;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileSystemView;

@SuppressWarnings("serial")
public class ProgressBarTest2 extends JPanel {
    private static final int PREF_W = 420;
    private static final int PREF_H = 200;
    private JProgressBar progressBar = new JProgressBar(0, 100);
    private DefaultListModel<File> fileListModel = new DefaultListModel<>();
    private JList<File> results = new JList<>(fileListModel);
    private Action browseAction = new BrowseAction("Browse", KeyEvent.VK_B);
    private JButton browseButton = new JButton(browseAction);

    public ProgressBarTest2() {
        progressBar.setValue(0);
        progressBar.setStringPainted(true);

        results.setCellRenderer(new MyCellRenderer());
        results.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        results.setVisibleRowCount(-1);
        JScrollPane scrollPane = new JScrollPane(results);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        JPanel topPanel = new JPanel();
        topPanel.add(browseButton);

        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        setLayout(new BorderLayout(3, 3));
        add(topPanel, BorderLayout.PAGE_START);
        add(scrollPane, BorderLayout.CENTER);
        add(progressBar, BorderLayout.PAGE_END);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension superSz = super.getPreferredSize();
        if (isPreferredSizeSet()) {
            return superSz;
        }
        int prefW = Math.max(superSz.width, PREF_W);
        int prefH = Math.max(superSz.height, PREF_H);
        return new Dimension(prefW, prefH);
    }

    public void workerIsDone() {
        browseAction.setEnabled(true);
    }


    private class BrowseAction extends AbstractAction {
        private FileWorker fileWorker;

        public BrowseAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            progressBar.setValue(0);

            JFileChooser chooser = new JFileChooser(System.getProperty("user.home"));
            chooser.setDialogTitle("Search what?");
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            int choice = chooser.showOpenDialog(null);
            if (choice != JFileChooser.APPROVE_OPTION) {
                return;
            }

            File file = chooser.getSelectedFile();
            if (file == null) {
                return;
            }
            if (!file.isDirectory()) {
                file = file.getParentFile();
                if (file == null || !file.isDirectory()) {
                    return;
                }
            }

            fileWorker = new FileWorker(file);
            fileWorker.addPropertyChangeListener(new FileWorkerListener());
            fileWorker.execute();
        }
    }

    class FileWorkerListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
                int progress = (int) evt.getNewValue();
                progressBar.setValue(progress);
            } else if ("state".equals(evt.getPropertyName())) {
                if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
                    workerIsDone();
                    FileWorker worker = (FileWorker) evt.getSource();
                    try {
                        worker.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    class FileWorker extends SwingWorker<Void, File> {
        private MyFileFilter dirFilter = new MyFileFilter(true);
        private MyFileFilter nonDirFilter = new MyFileFilter(false);
        private File file;

        public FileWorker(File file) {
            this.file = file;
        }

        @Override
        protected Void doInBackground() throws Exception {
            fileListModel.clear();
            File[] files = file.listFiles(nonDirFilter);
            for (File file2 : files) {
                publish(file2);
            }
            File[] dirs = file.listFiles(dirFilter);
            for (int i = 0; i < dirs.length; i++) {
                recurseThruDirs(dirs[i]);
                int prog = (100 * i) / dirs.length;
                setProgress(prog);
            }

            setProgress(100);
            return null;
        }

        private void recurseThruDirs(File file) {
            if (file == null) {
                return;
            }
            File[] files = file.listFiles(nonDirFilter);
            if (files != null && files.length > 0) {
                for (File file2 : files) {
                    publish(file2);
                }
            }
            File[] dirs = file.listFiles(dirFilter);
            if (dirs != null && dirs.length > 0) {
                for (int i = 0; i < dirs.length; i++) {
                    recurseThruDirs(dirs[i]);
                }
            }
        }

        @Override
        protected void process(List<File> chunks) {
            for (File file : chunks) {
                fileListModel.addElement(file);
            }
        }
    }

    class MyFileFilter implements FileFilter {
        private boolean directory;

        public MyFileFilter(boolean directory) {
            this.directory = directory;
        }

        @Override
        public boolean accept(File pathname) {
            return directory == pathname.isDirectory();
        }
    }

    private class MyCellRenderer extends DefaultListCellRenderer {
        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
                boolean isSelected, boolean cellHasFocus) {
            Component c = super.getListCellRendererComponent(list, value, index, isSelected,
                    cellHasFocus);
            JLabel l = (JLabel) c;
            File f = (File) value;
            l.setText(f.getName());
            l.setPreferredSize(new Dimension(130, 20));
            l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
            l.setBorder(new EmptyBorder(3, 3, 3, 3));

            return l;
        }
    }

    private static void createAndShowGui() {
        ProgressBarTest2 mainPanel = new ProgressBarTest2();

        JFrame frame = new JFrame("ProgressBarTest2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

【讨论】:

    猜你喜欢
    • 2011-10-30
    • 1970-01-01
    • 2011-02-18
    • 2010-10-02
    • 1970-01-01
    • 2023-03-08
    • 2011-07-29
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多