0

我想停止使用 jToggleButton 以下列方式生成的线程。线程用于查看文件夹中的文件。我尝试了很多,搜索了很多,但没有成功。任何机构都可以提供帮助并提出任何解决方案来停止生成类似的线程。即使在按下 jToggleButton 后,线程在 Netbeans 调试中仍显示为活动状态。我已经尝试过用于停止的不稳定条件,仅供参考:我有一个用于启动和停止线程的 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")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    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();
}// </editor-fold>                        

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

4 回答 4

3

使用线程中断状态

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

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

例子

这是使用的示例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,您将看到它们继续运行。

有问题的代码

目前的代码存在一些问题。下面是对事件处理程序的分析,该处理程序在按下按钮时由 Swing 事件调度线程( EDTjToggleButton1ActionPerformed() )调用。

按下按钮时,
  创建一个新的 ExecutorService 作为局部变量。
  如果选择切换,
    向执行者提交一个文件监视任务,并且
    阻塞 EDT 1 秒,或直到执行器关闭。
  否则,
    关闭新创建的执行器。
  放弃对执行者的引用。

第一个问题是,由于 executor 服务除了本地变量之外永远不会存储在任何地方,一旦该方法退出,对该特定实例的引用将永远丢失,并且无法调用shutdownNow()它。

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

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

流程应该是这样的:

按下按钮时,
  如果选择切换,
    创建一个新的执行器服务并将其分配给一个成员变量,并且
    向服务提交文件监视任务。
  否则,
    关闭执行器,并且
    等待服务终止。

另外,对于您在这里的使用, anewSingleThreadedExecutor()就足够了。

于 2013-09-10T16:12:35.323 回答
2

一种方法是使用将 astop设置为volatile boolean的方法true

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 进程,它也应该中断线程。

于 2013-09-10T15:03:20.557 回答
1

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

您可以修改一些变量来指示目标线程应该停止运行,而不是使用不推荐使用的方法。

于 2013-09-10T14:57:55.007 回答
1

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

于 2013-09-10T15:01:05.733 回答