中断或停止后线程不会停止

时间:2015-08-12 21:02:12

标签: java multithreading swing user-interface

我在停止线程时遇到了一些麻烦。我有扫描文件夹中文件的方法。我想有按钮停止扫描。点击此按钮后线程将停止(扫描将停止)并且程序将重置。我已经尝试过命令thread.stop();并且它有时工作(它不再工作了)。所以我在这里阅读了有关stackoverflow的一些主题并尝试thread.interrupt();我的代码看起来像这样:

public class myApp extends javax.swing.JFrame {
  Thread thread;
  int fileCount = 0;
  String path = "C:\\Program Files";

    public void scanningMethod() {
     thread = new Thread(new Runnable() {
        public void run() {
         while(!thread.interrupted()){
             //Recursion method that counting files
             dirScan(path);
           }  
        }    
     });
   thread.start();
  }

  private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {
      //generated button in netBeans
      thread.interrupt();
      thread.stop();
   }

 private void dirScan(String dirPath) {
    File[] podSoubory = new File(dirPath).listFiles();

    for (int i = 0; i < podSoubory.length; i++) {
        if (podSoubory[i].isDirectory()) {
            String tempPath = podSoubory[i].getAbsolutePath();
            System.out.println(tempPath);

        if (podSoubory[i].isFile()) {
            fileCount++;
        }
    }
  }
}

另一种停止方法仅包含thread.interrupt();(其中包含actionListener和stuff)。

可能我做错了什么。你能帮助我并告诉我如何在点击按钮后停止这个运行的线程吗? (我知道如何创建可点击按钮)。

3 个答案:

答案 0 :(得分:3)

关于你的主要问题,为什么你的代码不能正常工作,我很抱歉,但我没有答案,因为不幸的是,我没有时间去分析你的所有代码码。我会说你的是危险的代码(如你所知),不仅仅是因为你的调用setText(...)而且由于你的代码不遵守Swing线程规则,这些规则要求大多数Swing调用都是在Swing事件线程。您正在调用一些关键的Swing方法,包括后台线程中的cancel(true)方法,这会导致间歇性的调试线程错误。

我建议:

  • 出于几个原因,将SwingWorkers用作后台线程,但主要是因为您可以将后台线程中的数据安全地发布/处理到GUI中。
  • 如果要杀死SwingWorker,请调用其true方法,传入isCancelled()参数以允许此调用取消正在运行的代码。
  • 在SwingWorker中,检查publish(...)州。
  • 您希望后台线程的任何文本或数据都应传递到SwingWorker的doInBackground()方法中的process(...)调用中。
  • 然后,SwingWorker可以在其import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.*; public class InterruptedGui { private static final int GAP = 3; private MyWorker myWorker; private StartWorkerAction startWorkerAxn = new StartWorkerAction(this, "Worker"); private StopAllAction stopAllAction = new StopAllAction(this, "Stop All"); private JLabel statusLabel = new JLabel(""); private JTextArea finalTextArea = new JTextArea(20, 40); private JPanel mainPanel = new JPanel(); public InterruptedGui() { finalTextArea.setFocusable(false); JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0)); buttonPanel.add(new JButton(startWorkerAxn)); buttonPanel.add(new JButton(stopAllAction)); JPanel statusPanel = new JPanel(); statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.LINE_AXIS)); statusPanel.add(new JLabel("Status: ")); statusPanel.add(statusLabel); mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); mainPanel.setLayout(new BorderLayout()); mainPanel.add(buttonPanel, BorderLayout.PAGE_START); mainPanel.add(new JScrollPane(finalTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); mainPanel.add(statusPanel, BorderLayout.PAGE_END); } public JComponent getMainPanel() { return mainPanel; } public void setStatus(String text) { statusLabel.setText(text); } public MyWorker getMyWorker() { return myWorker; } public void setMyWorker(MyWorker myWorker) { this.myWorker = myWorker; } public void finalTextAreaSetText(String text) { finalTextArea.setText(text); } private static void createAndShowGui() { JFrame frame = new JFrame("Interrupted Gui"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new InterruptedGui().getMainPanel()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } class MyWorker extends SwingWorker<String, String> { private static final long SLEEP_TIME = 100; private static final int MAX_COUNTER = 100; private int counter = 0; private InterruptedGui gui; public MyWorker(InterruptedGui gui) { this.gui = gui; } @Override protected String doInBackground() throws Exception { StringBuilder sb = new StringBuilder(); while (counter < MAX_COUNTER && !isCancelled()) { counter++; String statusText = "Counter is " + counter; sb.append(statusText + "\n"); publish(statusText); Thread.sleep(SLEEP_TIME); } return sb.toString(); } @Override protected void process(List<String> chunks) { for (String statusText : chunks) { gui.setStatus(statusText); } } } @SuppressWarnings("serial") class StartWorkerAction extends AbstractAction { private InterruptedGui gui; public StartWorkerAction(InterruptedGui gui, String name) { super(name); this.gui = gui; int mnemonic = (int) name.charAt(0); putValue(MNEMONIC_KEY, mnemonic); } @Override public void actionPerformed(ActionEvent e) { MyWorker myWorker = gui.getMyWorker(); if (myWorker != null && !myWorker.isDone()) { return; // still running current worker } gui.finalTextAreaSetText(""); myWorker = new MyWorker(gui); gui.setMyWorker(myWorker); myWorker.addPropertyChangeListener(new WorkerPropertyListener(gui)); myWorker.execute(); } } class WorkerPropertyListener implements PropertyChangeListener { private InterruptedGui gui; public WorkerPropertyListener(InterruptedGui gui) { this.gui = gui; } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() == SwingWorker.StateValue.DONE) { MyWorker myWorker = gui.getMyWorker(); if (myWorker != null && !myWorker.isCancelled()) { try { String finalText = myWorker.get(); gui.finalTextAreaSetText(finalText); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } } } @SuppressWarnings("serial") class StopAllAction extends AbstractAction { private InterruptedGui gui; public StopAllAction(InterruptedGui gui, String name) { super(name); this.gui = gui; int mnemonic = (int) name.charAt(0); putValue(MNEMONIC_KEY, mnemonic); } @Override public void actionPerformed(ActionEvent e) { MyWorker myWorker = gui.getMyWorker(); if (myWorker == null) { return; } myWorker.cancel(true); } } 方法中处理这些数据,这种方法可以保证在Swing事件线程上调用。

例如:

CHARACTER SET

答案 1 :(得分:3)

您对线程中断的工作方式存在误解,当您调用Thread#interrupt时,所有发生的事件都会在flag实例中引发Thread,您可以使用{interrupted实例进行检查1}}或isInterrupted

在您的代码中,您有一个for-loop

for (int i = 0; i < podSoubory.length; i++) {
    if (podSoubory[i].isDirectory()) {
        String tempPath = podSoubory[i].getAbsolutePath();
        System.out.println(tempPath);

    if (podSoubory[i].isFile()) {
        fileCount++;
    }
}

这意味着在此for-loop存在之前,while(!thread.interrupted()){语句不会被评估。

您需要做的是在代码中的周期点测试isInterrupted,例如......

for (int i = 0; i < podSoubory.length && !Thread.currentThread().isInterrupted(); i++) {
    if (podSoubory[i].isDirectory()) {
        String tempPath = podSoubory[i].getAbsolutePath();
        System.out.println(tempPath);

        if (podSoubory[i].isFile()) {
            fileCount++;
        }
    }
}

这会添加对isInterrupted的检查,导入为isInterrupted WON&#39;清除像Thread#interrupted这样的中断标记,这样可以让您的代码的其他部分更进一步也测试interrupted标志。

当您中断Thread时,for-loopwhile-loop都会检查interrupted标志的状态,并允许两个循环退出。

作为一个可运行的例子......

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class MyApp {

    public static void main(String[] args) {
        new MyApp();
    }

    public MyApp() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {

            setLayout(new GridBagLayout());
            JButton stop = new JButton("Stop");
            stop.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    thread.interrupt();
                    // Join is used here to prove a point, be careful
                    // with using this within the context of the EDT
                    try {
                        thread.join();
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    stop.setEnabled(false);
                }
            });
            add(stop);

            scanningMethod();
        }

        Thread thread;
        int fileCount = 0;
        String path = "C:\\Program Files";

        public void scanningMethod() {
            thread = new Thread(new Runnable() {
                public void run() {
                    while (!thread.isInterrupted()) {
                        //Recursion method that counting files
                        dirScan(path);
                        System.out.println(thread.isInterrupted());
                    }
                }
            });
            thread.start();
        }

        private void dirScan(String dirPath) {
            File[] podSoubory = new File(dirPath).listFiles();

            for (int i = 0; i < podSoubory.length && !Thread.currentThread().isInterrupted(); i++) {
                if (podSoubory[i].isDirectory()) {
                    String tempPath = podSoubory[i].getAbsolutePath();
                    System.out.println(tempPath);

                    if (podSoubory[i].isFile()) {
                        fileCount++;
                    }
                }
            }

        }
    }

}

您可能需要查看Concurrency in Java了解更多详情

另外,Thread#stop已被弃用,绝不应该使用JavaDocs ...

  

<强>已过时即可。这种方法本质上是不安全的。使用停止线程   Thread.stop使它解锁已锁定的所有监视器   (作为未经检查的ThreadDeath例外的自然结果   传播堆栈)。如果以前受保护的任何物体   由这些监视器处于不一致状态,受损物体   对其他线程可见,可能导致任意   行为。 stop的许多用法应该简单地用代码替换   修改某个变量以指示目标线程应该停止   运行。目标线程应定期检查此变量,并且   如果变量,则以有序的方式从其run方法返回   表示它将停止运行。如果目标线程等待   长期(例如,在条件变量上),中断   应该使用方法来中断等待。有关更多信息,请参阅   Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

答案 2 :(得分:-1)

我建议通过Runnable接口实现一个线程。在这种情况下,您需要实现run()和kill()方法。将本地共享变量作为标志引入。在kill方法中,将标志设置为false,在run方法中执行操作,直到标志为真。

一般情况下,我建议熟悉在这种情况下可能对您有用的等待/通知机制以及Java Executioner框架。

最重要的是,请搜索stackoverflow以获取类似问题,之前已经回答过。例如,Java: How to stop thread?

希望这有帮助。