繁体   English   中英

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

[英]Thread won't stop after interrupting or stopping

我在停止线程方面遇到了一些麻烦。 我有扫描文件夹中文件的方法。 我想要一个按钮来停止此扫描。 点击此按钮后,线程将停止(扫描将停止),程序将重置。 我试过命令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(); (ofc中有actionListener和东西)。

可能是我做错了。 您可以帮助我,并告诉我如何在单击按钮后停止该正在运行的线程吗? (我知道如何创建可点击的按钮)。

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

我建议:

  • 出于多种原因,将SwingWorkers用于后台线程,但主要是为了使您可以将后台线程中的数据安全地发布/处理到GUI中。
  • 如果您想杀死SwingWorker,请调用其cancel(true)方法,并传入true参数以允许此调用取消正在运行的代码。
  • 在您的SwingWorker中,检查isCancelled()状态。
  • 您想要背景线程的任何文本或数据都应传递到SwingWorker的doInBackground()方法内的publish(...)调用中。
  • 然后,SwingWorker可以在其process(...)方法内处理此数据,该方法可以保证在Swing事件线程上被调用。

例如:

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

您对线程中断的工作方式有一个误解,当您调用Thread#interrupt ,发生的一切只是在Thread实例中引发了一个flag ,您可以使用interruptedisInterrupted检查该flag

在您的代码中,您有一个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不会像Thread#interrupted will那样清除中断标志,这也允许代码的其他部分也进一步测试interrupted标志。

当您中断Threadfor-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++;
                    }
                }
            }

        }
    }

}

您可能需要看一下Java中的并发性以获得更多详细信息。

此外,从JavaDocs开始 ,不建议使用Thread#stop ,并且永远不要使用Thread#stop

不推荐使用 这种方法本质上是不安全的。 使用Thread.stop停止线程会导致它解锁所有已锁定的监视器(由于未经检查的ThreadDeath异常会在堆栈中传播,这是自然的结果)。 如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。 stop的许多用法应由仅修改某些变量以指示目标线程应停止运行的代码代替。 目标线程应定期检查此变量,如果该变量指示要停止运行,则应按有序方式从其运行方法返回。 如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待。 有关更多信息,请参见为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?

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

通常,我建议您熟悉等待/通知机制以及Java Executioner框架,在这种情况下可能对您有所帮助。

最重要的是,请在stackoverflow中搜索类似的问题,这些问题已在之前得到解答。 例如, Java:如何停止线程?

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM