How to stop threads of following type in Java used for watching folders for files using WatchService for folders by using jToggleButton

后端 未结 4 621
日久生厌
日久生厌 2021-01-17 03:14

I would like to stop threads generated in the following manner by using jToggleButton. The threads are used for watching folders for files. I tried a lot, and searched alot,

相关标签:
4条回答
  • 2021-01-17 03:50

    You can use some flag inside the run method to check whether to exit the method or not, by this way you can indirectly exit the run method. Stopping a thread by any other method is currently not advisable. See link

    0 讨论(0)
  • 2021-01-17 03:54

    One way would be to use a stop method that sets a volatile boolean to 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();
        }
      }
    }
    

    If the thread might be blocked you can arrange to interrupt it but of course this is not guaranteed to interrupt the thread as it may not be blocked, just busy.

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

    This last technique should also work if using WatchService.poll(...) or WatchService.take().

    It should also interrupt the thread if it is busy with most IO processes.

    0 讨论(0)
  • 2021-01-17 03:54

    There is a Thread.stop() method, but is has been deprecated, because it's unsafe.

    Instead of using the deprecated method(s), you can modify some variable to indicate that the target thread should stop running.

    0 讨论(0)
  • 2021-01-17 04:12

    Use thread interruption status

    Use the interruption state of the thread to terminate a loop. This is better than a flag you create yourself because it makes your task usable with an ExecutorService; you can cancel a specific task through the Future you received when it is submitted, or you can interrupt all tasks with shutdownNow().

    Unless your task is running in a thread you create and manage yourself, it's safest to re-assert the interruption status after detecting interruption, so that your caller can deal with it too. In other words, all threads and tasks need to have a defined interruption policy and to be used accordingly.

    Example

    Here's an example Runnable task using 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());
          }
        }
      }
    }
    

    Here's how you could use such a service:

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

    Because the FileWatch task correctly supports interruption, you will see that this test shows all tasks are completed soon after shutdownNow() is called. If you add tasks that use some other termination method to the ExecutorService, you will see that they continue to run.

    The code in question

    There are a couple of problems with the code as it stands currently. Here's analysis of the jToggleButton1ActionPerformed() event handler, which is invoked by the Swing Event Dispatch Thread (the EDT) when the button is pressed.

    When the button is pressed,
      create a new ExecutorService as a local variable.
      If toggle selected,
        submit a file watching task to the executor, and
        block the EDT for 1 second, or until the executor is shutdown.
      Otherwise,
        shutdown the newly-created executor.
      Discard reference to the executor.
    

    The first problem is that since the executor service is never stored anywhere but a local variable, once that method exits, a reference to that particular instance is lost forever, and there's no way to invoke shutdownNow() on it.

    The second problem is that, if really want block the EDT (probably not a good idea) until the executor terminates, you should do it after invoking shutdownNow() (in your "else" clause, when toggle is unselected), not after submitting the task. Look at my example above again and you'll see that I await termination only after I shutdown the executor.

    Hoist the ExecutorService variable out of the method, and make it a member variable of your class. This will allow the toggle button handler to access the same instance of the ExecutorService and shut it down. Then, move the wait for termination to the unselected toggle branch.

    This is what the flow should be:

    When the button is pressed,
      If toggle selected,
        create a new executor service and assign it to a member variable, and
        submit a file watching task to the service.
      Otherwise,
        shutdown the executor, and
        wait for the service to terminate.
    

    Also, for your use here, a newSingleThreadedExecutor() would suffice.

    0 讨论(0)
提交回复
热议问题