Domanda

I try to end a ExecutorService with executorService.shutdown(); and if that didn't worked, with executorService.shutdown();. The Problem is, that the executorService can't be stopped and the program is still running.

Here is my class which shall start a WatchService to watch a directory for changes:

public class FileWatcher {
    /**
     * This Class starts a WatchService to get changes on a specific folder.
     */

    Path dir;

    private final ConfigManager configManager;

    private final FileHandler fileHandler;

    private ExecutorService executorService;

    public FileWatcher(ConfigManager configManager, FileHandler fileHandler) {
        this.configManager = configManager;
        this.fileHandler = fileHandler;
    }

    public void start() {
        dir = configManager.getConfig().getJdfPath();

        //executorService = Executors.newFixedThreadPool(1);
        executorService = Executors.newSingleThreadExecutor();
        Runnable runWatcher;
        runWatcher = new Runnable() {
            @Override
            public void run() {
                try {
                    startWatcher();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        executorService.execute(runWatcher);
    }

    private void startWatcher() throws IOException, InterruptedException {
        /**
         * Create a new WatchService which detects created and modified files. To
         * let it detect deleted files add ENTRY_DELETE to dir.register().
         */

        WatchService watcher = FileSystems.getDefault().newWatchService();
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);

        while (!Thread.currentThread().isInterrupted()) {
            key = waitForEvents(watcher, key);

        }
    }

    private WatchKey waitForEvents(WatchService watcher, WatchKey key) {

        /**
         * The WatchService tells the FileHandler the Filename of the changed or
         * new file in the given folder.
         */

        try {
            key = watcher.take();
        } catch (InterruptedException e) {
            executorService.shutdown();

        } catch (ClosedWatchServiceException e) {
            executorService.shutdown();

        }

        for (WatchEvent<?> event : key.pollEvents()) {

            fileHandler.setPath((Path) event.context());
        }
        key.reset();
        return key;
    }

    public void stop() {
        stopWatcher();
    }

    private void stopWatcher() {
        executorService.shutdown(); // Disable new tasks from being submitted
        try {
            // Wait a while for existing tasks to terminate
            if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                executorService.shutdownNow(); // Cancel currently executing tasks
                // Wait a while for tasks to respond to being cancelled
                if (!executorService.awaitTermination(10, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (InterruptedException ie) {
            // (Re-)Cancel if current thread also interrupted
            executorService.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }
} 

I got the method stopWatcher() from the oracle java api]1. I wrote a simple class to test the FileWatcher class.

public class TestFileWatcher {
    static Path path;
    static ConfigManager configmanager;

    public static void main(String[] args) {

        path = Paths.get("D:/testfolder");
        FileHandler filehandler = new FileHandler() {

            @Override
            public void setPath(Path path) {
                System.out.println("file: " + path);

            }

            @Override
            public Path getPath() {
                // TODO Auto-generated method stub
                return null;
            }
        };

        final Config config;
        config = new Config() {

            @Override
            public Path getJdfPath() {
                // TODO Auto-generated method stub
                return path;
            }
        };

        configmanager = new ConfigManager() {

            @Override
            public void injectConfig(Config config) {
                // TODO Auto-generated method stub

            }

            @Override
            public Config getConfig() {
                // TODO Auto-generated method stub
                return config;
            }
        };



        configmanager.injectConfig(config);
        filehandler.setPath(path);
        final FileWatcher filewatcher = new FileWatcher(configmanager, filehandler);
        filewatcher.start();
        Timer timer = new Timer();


        boolean stopped;

        TimerTask closeWatcherTask = new TimerTask(){
            @Override
            public void run() {

                System.out.println("filewatcher stopped.");
                filewatcher.stop();

            }
        };
        long duration = 2000;
        timer.schedule(closeWatcherTask, duration);
        return;

    }

}

So, when I start the Test Application to start the FileWatcher and end it 2 seconds after that, the program tells me: Pool did not terminate. What can I change, to terminate it correctly?

By the way, the ConfigManager and FileHandler are interfaces, where I get the Path which I have to whatch for changes.

È stato utile?

Soluzione 2

shutDown() will only tell the executor to not accept any new tasks & cancels all tasks that are not runnuing yet. Tasks that are running are allowed to finish.

You are looping indefinitely in

    while (!Thread.currentThread().isInterrupted()) {
        key = waitForEvents(watcher, key);
    }

because an InterruptedException is not setting the interrupted flag.

If you change

    try {
        key = watcher.take();
    } catch (InterruptedException e) {
        executorService.shutdown();
    } catch (ClosedWatchServiceException e) {
        executorService.shutdown();
    }

To

    try {
        key = watcher.take();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } catch (ClosedWatchServiceException e) {
        Thread.currentThread().interrupt();
    }

and add executorService.shutdown(); after the while loop (or keep it where it is) you should have no problem.

Altri suggerimenti

Without analyzing your code it does not shutdown because any of the threads have not finished/are holding resources. The easy dirty solution is to do a force stop with ExecuterService.shutdownNow() which states in javadoc:

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

So try this and analyze the List of Runnables returned by ExecuterService.shutdownNow() you get back to see which thread is hanging and then do a step by step debug and see what is causing the hang.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top