Метод будущей отмены (логическое значение b) не работает при использовании в приложении с графическим интерфейсом

У меня такой код:

public class Cancelling {
public static void main(String args[]) {
    ToBeCancelled tbc = new ToBeCancelled();
    ForkJoinPool pool = new ForkJoinPool(1);
    Future<?> future = pool.submit(tbc);

    try {
        Thread.sleep(3000);
    } catch (InterruptedException ie) {}

    future.cancel(true);
    if (future.isCancelled())
        System.out.println("The task has been cancelled");
}
}

С классом ToBeCancelled:

public class ToBeCancelled implements Runnable {
public void run() {
    try {
        Thread.sleep(5000); // should throw exception here
    } catch (Exception e) {
        return; // should exit
    }

    System.out.println("I should never be able to print this");
}
}

Основной поток должен запуститься, подождать 3 секунды, а затем отменить задачу ToBeCancelled, используя future.cancel(true). Затем он должен напечатать The task has been cancelled, в то время как задача никогда не сможет распечатать свое сообщение. По крайней мере, это то, что происходит, когда я запускаю его с консоли.

Поскольку я запускаю его из приложения с графическим интерфейсом пользователя с TextArea, куда перенаправляется вывод, это не так. Основной метод печатает The task has been cancelled, но задача также печатает I should never be able to print this.

Это сводит меня с ума. Насколько я понимаю, задача должна получить свою команду cancel при использовании метода Thread.sleep(5000), который вызовет исключение, которое, следовательно, будет перехвачено и приведет к возврату потока. Но этого не происходит, а главное думает, что его отменили. Как будто метод cancel полностью игнорируется задачей.

Я перепробовал все, что мог придумать, проверяя возвращаемое значение cancel, заставляя задачу ждать дольше, используя Thread.currentThread().isInterrupted(), но ничего не работает.

Мне кажется, что мне не хватает чего-то очень простого, но я просто не могу найти, что это такое. Любая идея?

Если кто-то думает, что это может быть что-то в приложении с графическим интерфейсом, это метод, который запускает программу:

public static void StartProgram(String name) {
    try {
        Method m = Class.forName(name).getDeclaredMethod("main",String[].class);
        Object[] args = new Object[1];
        String s[] = new String[2];
        s[0] = tf1.getText();
        s[1] = tf2.getText();
        args[0] = s;
        t = new Thread(new ProgramStarter(args, m));
        t.start();
    } catch (Exception e) {
        e.printStackTrace();
    }       
}

При этом ProgramStarter:

public class ProgramStarter implements Runnable {
private Object[] args;
private Method m;

public ProgramStarter(Object args[], Method m) {
    this.args = args;
    this.m = m;
}

public void run() {
    try {
        m.invoke(null, args);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

person user3648026    schedule 18.05.2014    source источник
comment
Возможно ли, что прошло достаточно времени, и поток выходит из спящего режима до прерывания, вызвавшего исключение? Попробуйте увеличить время сна.   -  person Sotirios Delimanolis    schedule 18.05.2014
comment
Я пробовал это, и это ничего не меняет. Я также уверен, что это не так, потому что, когда основная программа печатает Задача была прервана, это означает, что она уже вызвала метод отмены, и через 2 секунды после этого задача говорит, что я никогда не смогу распечатать это.   -  person user3648026    schedule 18.05.2014


Ответы (1)


Проблема в том, что ваша проверка неверна. Вы думаете, что ваш код работает при запуске из консоли, но на самом деле он дает сбой во всех случаях. При запуске с консоли ваш основной поток завершается после попытки отменить будущее, и JVM будет завершена, поскольку в JVM остались только потоки демона. Из-за прекращения работы JVM вы не заметили, что отмена не сработала.

При добавлении sleep в конец вашего main метода для задержки завершения работы JVM вы заметите, что "I should never be able to print this" также печатается при запуске из консоли. Таким образом, единственная разница между версией с графическим интерфейсом пользователя и консольной версией заключается в том, что запущенный поток диспетчеризации событий предотвращает завершение работы JVM, поэтому вы видите, что она не работает.


Суть в следующем: не используйте ForkJoinPool , если у вас нет для этого причины.

Поскольку вам просто нужен submit для простого однопоточного исполнителя, вы можете создать исполнителя с помощью Executors.newFixedThreadPool(1). Это менее неожиданное поведение: этот поток по умолчанию не является демоном, и он Future будет cancel с прерыванием, как и ожидалось.

person Holger    schedule 19.05.2014
comment
Ага, вот и все. Добавление сна заставило его распечатать это и в консоли, и Executors решили мою проблему. Спасибо. - person user3648026; 19.05.2014