如何使用jToggleButton在Java中停止以下类型的线程,用于使用WatchService查看文件夹的文件夹

我想通过使用jToggleButton来停止以下面的方式生成的线程。 线程用于监视文件的文件夹。 我尝试了很多,并搜索了很多,但没有成功。 可以任何身体帮助并建议任何解决方案来阻止生成的线程。 即使在按下jToggleButton之后,线程也会在Netbeans调试中显示为活动状态。 我已经尝试了停止的易失性条件,FYI:我有一个用于启动和停止线程的jToggle按钮。

代码由Netbeans生成,因此有一些额外的代码,但您可能只关注jToggleActionListener中的代码和另一个类中的代码:感谢您的帮助。

package threadnames; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; public class NewJFrame extends javax.swing.JFrame { public NewJFrame() { initComponents(); } @SuppressWarnings("unchecked") //  private void initComponents() { jToggleButton1 = new javax.swing.JToggleButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jToggleButton1.setText("Stop"); jToggleButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jToggleButton1ActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(84, 84, 84) .addComponent(jToggleButton1) .addContainerGap(142, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(25, 25, 25) .addComponent(jToggleButton1) .addContainerGap(28, Short.MAX_VALUE)) ); pack(); }//  private void jToggleButton1ActionPerformed(java.awt.event.ActionEvent evt) { ExecutorService exec = Executors.newCachedThreadPool(); if (this.jToggleButton1.isSelected()) { try { // TODO add your handling code here: Path home = Paths.get(System.getProperty("user.dir")); WatchService watcher; watcher = home.getFileSystem().newWatchService(); home.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); Runnable task = new FileWatch(watcher); exec.submit(task); boolean terminated; terminated = exec.awaitTermination(1, TimeUnit.SECONDS); if (terminated) { System.out.println("All tasks completed."); } else { System.out.println("Some tasks are still running."); } } catch (IOException | InterruptedException ex) { Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex); } } else { exec.shutdownNow(); } } public static void main(String args[]) { try { for (javax.swing.UIManager.LookAndFeelInfo info javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(NewJFrame.class .getName()).log(java.util.logging.Level.SEVERE, null, ex); } java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new NewJFrame().setVisible(true); } }); } // Variables declaration - do not modify public javax.swing.JToggleButton jToggleButton1; // End of variables declaration } 

这是run()的另一个类:

 package threadnames; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.nio.file.Watchable; final class FileWatch implements Runnable { private final WatchService watcher; FileWatch(WatchService watcher) { this.watcher = watcher; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { WatchKey key; try { key = watcher.take(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); break; } Watchable dir = key.watchable(); System.out.println(dir); for (WatchEvent evt : key.pollEvents()) { System.out.println(" " + evt.context()); } } } } 

使用线程中断状态

使用线程的中断状态来终止循环。 这比您自己创建的标志更好,因为它使您的任务可以与ExecutorService一起使用; 您可以通过提交时收到的Future取消特定任务,也可以使用shutdownNow()中断所有任务。

除非您的任务在您自己创建和管理的线程中运行,否则在检测到中断后重新断言中断状态是最安全的,这样您的调用者也可以处理它。 换句话说,所有线程和任务都需要具有已定义的中断策略并相应地使用。

以下是使用WatchService Runnable任务WatchService

 final class FileWatch implements Runnable { private final WatchService watcher; FileWatch(WatchService watcher) { this.watcher = watcher; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { WatchKey key; try { key = watcher.take(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); break; } Watchable dir = key.watchable(); System.out.println(dir); for (WatchEvent evt : key.pollEvents()) { System.out.println(" " + evt.context()); } } } } 

以下是您可以使用此类服务​​的方法:

 public static void main(String... argv) throws Exception { Path home = Paths.get(System.getProperty("user.home")); WatchService watcher = home.getFileSystem().newWatchService(); home.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.OVERFLOW); Runnable task = new FileWatch(watcher); ExecutorService exec = Executors.newCachedThreadPool(); exec.submit(task); Thread.sleep(3000); exec.shutdownNow(); boolean terminated = exec.awaitTermination(1, TimeUnit.SECONDS); if (terminated) System.out.println("All tasks completed."); else System.out.println("Some tasks are still running."); } 

由于FileWatch任务正确支持中断,因此您将看到此测试显示在调用shutdownNow()后不久就完成了所有任务。 如果将使用其他终止方法的任务添加到ExecutorService ,您将看到它们继续运行。

有问题的代码

目前代码存在一些问题。 这里是对jToggleButton1ActionPerformed()事件处理程序的分析,该处理程序在按下按钮时由Swing Event Dispatch Thread( EDT )调用。

按下按钮时
  创建一个新的ExecutorService作为局部变量。
  如果选中切换,
    向执行者提交文件观看任务,以及
    阻止EDT 1秒,或直到执行器关闭。
  除此以外,
    关闭新创建的执行程序。
  放弃对执行者的引用。

第一个问题是,由于执行器服务永远不会存储在除局部变量之外的任何地方,因此一旦该方法退出,对该特定实例的引用将永远丢失,并且无法对其调用shutdownNow()

第二个问题是,如果真的想要阻止EDT(可能不是一个好主意),直到执行程序终止,你应该在调用shutdownNow() (在你的“else”子句中,当取消选择切换时),而不是在提交之后任务。 再看看上面的例子,你会看到我在关闭执行程序后等待终止。

将ExecutorService变量从方法中提取出来,并使其成为类的成员变量。 这将允许切换按钮处理程序访问ExecutorService 的同一实例并将其关闭。 然后,将等待终止移动到未选择的切换分支。

这就是流程应该是:

按下按钮时
  如果选中切换,
    创建一个新的执行器服务并将其分配给成员变量,和
    向服务提交文件观看任务。
  除此以外,
    关闭执行程序,然后
    等待服务终止。

此外,为了您的使用, newSingleThreadedExecutor()就足够了。

一种方法是使用将volatile booleantruestop方法。

 public class HelloRunnable implements Runnable { private volatile boolean stop = false; public void run() { if (!stop) { System.out.println("Hello from a thread!"); } } public void stop() { stop = true; } public static void main(String args[]) { for (int i = 0; i < 5; i++) { HelloRunnable hr = new HelloRunnable(); new Thread(hr).start(); hr.stop(); } } } 

如果线程可能被阻塞,你可以安排中断它,但当然不能保证中断线程,因为它可能没有被阻塞,只是忙。

 public class HelloRunnable implements Runnable { private volatile boolean stop = false; private volatile Thread thread = null; public void run() { thread = Thread.currentThread(); if (!stop) { System.out.println("Hello from a thread!"); } } public void stop() { stop = true; if ( thread != null ) { thread.interrupt(); } } public static void main(String args[]) { for (int i = 0; i < 5; i++) { HelloRunnable hr = new HelloRunnable(); new Thread(hr).start(); hr.stop(); } } } 

如果使用WatchService.poll(...)或WatchService.take(),最后一种技术也应该有效。

如果线程忙于大多数IO进程,它也应该中断线程。

有一个Thread.stop()方法,但已被弃用 ,因为它不安全。

您可以修改某个变量来指示目标线程应该停止运行,而不是使用已弃用的方法。

您可以在run方法中使用一些flag来检查是否退出该方法,这样您就可以间接退出run方法。 目前不建议通过任何其他方法停止线程。 见链接