Остановка потока и вывод результата в консоль по нажатию ESC

212
18 июля 2017, 18:27

Есть консольное приложение, которое считает количество файлов в n-м количестве папок одновременно. Необходимо по нажатию ESC, остановить все потоки и вывести результаты, которые уже посчитало.

Допустим, из 10000 файлов, насчитало 4000, к моменту нажатия на кнопку. И вывести эти 4000. Есть рабочая программа, но ума не приложу, каким образом по нажатию ESC в консоли остановить это все. В интернете нашел такой способ:

public class FileCounter extends Thread implements KeyListener {
    private File file;
    private String filePath;
    private int countOfFiles;
    boolean flag;
    public FileCounter(String file, String name) {
        this.filePath = file;
        this.file = new File(file);
        setName(name);
    }
    public int countFiles(File file) {
        int count = 0;
        flag = false;
        try {
            File[] folderEntries = file.listFiles();
            for (File entry : folderEntries) {
                if (entry.isDirectory()) {
                    count += countFiles(entry);
                    synchronized (this) {
                        while (flag) {
                            System.in.read();
                            wait();
                            return count;
                        }
                    }
                    continue;
                } else {
                    count++;
                    synchronized (this) {
                        while (flag) {
                            wait();
                            return count;
                        }
                    }
                }
            }
        }catch (InterruptedException e){
            System.out.println(e.getMessage());
        }
        catch (IOException e){
            System.out.println(e.getMessage());
        }
        return count;
    }
    public File getFile() {
        return file;
    }
    public void setFile(File file) {
        this.file = file;
    }
    public int getCountOfFiles() {
        return countOfFiles;
    }
    public String getFilePath() {
        return filePath;
    }
    @Override
    public void run() {
        this.countOfFiles = countFiles(this.file);
    }
    @Override
    public void keyTyped(KeyEvent e) {
    }
    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
            flag = true;
        }
    }
    @Override
    public void keyReleased(KeyEvent e) {
    }
}

Но у меня он не работает. Скорее всего потому, что в main прописан join на все потоки, что бы если не нажималось esc, вывести конечный результат.

public static void main(String[] args) throws FileNotFoundException {
    String target = "E:\\Test\\Test.txt";
    String result = "E:\\Test\\result.txt";
    List<String> parsedTarget = pathParser(target);
    List<Result> results = new ArrayList<>();
    FileCounter[] threads = new FileCounter[parsedTarget.size()];
    for (int i = 0; i < threads.length; i++) {
        threads[i] = new FileCounter(parsedTarget.get(i), String.format("Thread %d", i));
    }
    startThreads(threads);
    try {
        for (FileCounter thread : threads) {
            thread.join();
            if (!thread.isAlive()) {
                results.add(new Result(((int) thread.getId())-10, thread.getFilePath(), thread.getCountOfFiles()));
            } else if (stop) {
                results.add(new Result(((int) thread.getId()), parsedTarget.get(((int) thread.getId())), thread.getCountOfFiles()));
            }
        }
    }catch (InterruptedException e ){
        System.out.println(e.getMessage());
    }
    String res;
    for (Result result1 : results){
        res = result1.getNum() + " " + result1.getCount() + " " + result1.getPath() + ";";
        System.out.println(res);
        writeCSV(result,res);
    }
}
Answer 1

В циклах обхода по файловой системе нужно проверять флаг Thread.isInterrupted() и если флаг окажется true то выходить из цикла. Изменить этот флаг вы можете при помощи Thread.interrupted() для этого достаточно иметь ссылку на поток в месте где вы вызываете эту остановку. Только учитывайте что такие методы как sleep() и wait() пробуждаются и вызывают InterruptedException.

Более подробно можно прочитать здесь.

Итог:

1.Делайте список с потоками которые собирают файлы так чтобы ссылка на этот список была под рукой в том месте где вы будете реализовывать остановку Назовем его threadsFinder.

2.Добавляете в класс потоков проверку на isInterrupted() в цикле который собирает файлы.

while(!Thread.currentThread().isInterrupted() && естьЧтоСобирать) {
    ...Ваша логика работы с файлами...
}

3.В случае отказа пользователя от дальнейшего сбора файлов вызываете у всех потоков interrupted():

for(Thread t : threadsFinder) {
    t.interrupted();
}

Что приведет к тому что потоки собиратели выйдут из цикла.

4.Не забудьте что ваш список для собранных данных должен быть потокобезопасным.

READ ALSO
Как указать User-Agent?

Как указать User-Agent?

ЗдравствуйтеИмеется код:

206
Получение значения из компонента JSF

Получение значения из компонента JSF

Как передать значение из одного компонента в другой?

202
Facebook messenger проблема с получением Postback

Facebook messenger проблема с получением Postback

Пишу чат бот для Facebook и столкнулся с проблемой при получении payload с нажатой кнопки юзером

185
Построить пирамиду из символов

Построить пирамиду из символов

Написал метод, который строить пирамиду из символов "^"Саму пирамиду рисует правильно, только вот есть лишний пробел и переход на новую строку,...

729