如何同步两个线程,使他们都在Java中同时完成
问题描述:
我试图实现一个进度条,确定从Java中的另一个线程完成百分比(近似)。现在我有两个线程在同一时间运行,但我不确定如何链接到其他线程。我需要它从0开始,等到另一个朋友完成时,我想让进度条说100.这是我迄今为止所拥有的。我几乎整天都在寻找解决方案,但似乎找不到任何东西。建议?如何同步两个线程,使他们都在Java中同时完成
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事件线程之外。
- 如果您无法量化需要完成的工作量并且无法通过后台任务通知已完成此项工作的百分比,请考虑将您的JProgressBar设置为不确定。
- 否则使用确定的进度条,但让后台任务以间接方式通知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());
}
}
链接线程?我认为你对你需要做的事情感到困惑。您不希望链接线程,而只是简单地将后台线程上运行的一段代码的进程与Swing事件线程上运行的另一段代码进行通信。 –
您的代码也正在从后台线程进行Swing突变更改,这是您永远不应该做的。请阅读Swing线程教程,以了解如何正确处理此问题:[Lesson:Swing中的并发](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/)。 –
@HovercraftFullOfEels我是新来的Java语言和线程所有在一起。我只是将我在YouTube视频和一些示例中看到的内容展开。 – Vince