Отправка фоновой задачи из приложения spring mvc

У меня работает приложение spring MVC, и теперь я пытаюсь запустить или отправить фоновое задание из своего приложения.

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

Но также я хотел бы остановить/убить/приостановить задачу, если мне это нужно. Поскольку я не делал этого раньше, я ищу хороший/лучший способ сделать это.

Я нашел их полезными:

http://blog.springsource.com/2010/01/05/task-scheduling-simplifications-in-spring-3-0/

Как вы убиваете поток на Java?

Java-потоки: возможно ли просмотреть/приостановить/убить конкретный поток из другой java-программы, запущенной на той же JVM?

Итак, я хотел использовать задачу @Async для отправки моей фоновой задачи, но хотел использовать идентификатор потоков, чтобы получить ее позже, и остановить ее при необходимости?

Это правильный подход? У меня нет опыта многопоточности, поэтому я здесь, чтобы слушать.

Обновление кода:

public interface Worker {
    public void work();
    public void cancel();
}

реализация:

@Component("asyncWorker")
public class AsyncWorker implements Worker {

    @Async
    public void work() {
        String threadName = Thread.currentThread().getName();
        System.out.println("   " + threadName + " beginning work");
        try {
                Thread.sleep(10000); // simulates work
        } catch (InterruptedException e) {
            System.out.println("I stopped");
        }
        System.out.println("   " + threadName + " completed work");
    }

    public void cancel() { Thread.currentThread().interrupt(); }
}

Контроллер для тестирования:

@ResponseBody
@RequestMapping("/job/start")
public String start() {
    asyncWorker.work();
    return "start";
}

@ResponseBody
@RequestMapping("/job/stop")
public String stop() {
    asyncWorker.cancel();
    return "stop";
}

Когда я посещаю /job/start, я не могу выполнить еще одну задачу одновременно. Другой запускает выполнение только после завершения первого

Также, когда я посещаю /job/stop, процесс не останавливается, что мне здесь не хватает?

Ответ 1

Использование идентификатора потока слишком низкое и хрупкое. Если вы решили использовать @Async аннотация (хороший выбор), вы можете использовать Future<T>, чтобы контролировать выполнение задачи. В основном ваш метод должен возвращать Future<T> вместо void:

@Async
public Future<Work> work() //...

Теперь вы можете cancel() > Future или дождаться завершения:

@ResponseBody
@RequestMapping("/job/start")
public String start() {
    Future<Work> future = asyncWorker.work();
    //store future somewhere
    return "start";
}

@ResponseBody
@RequestMapping("/job/stop")
public String stop() {
    future.cancel();
    return "stop";
}

Сложная часть - сохранить возвращенный объект Future так или иначе, чтобы он был доступен для последующих запросов. Конечно, вы не можете использовать поле или ThreadLocal. Вы можете поместить сеанс, обратите внимание, однако, что Future не является сериализуемым и не будет работать через кластеры.

Так как @Async обычно поддерживается пулом потоков, возможно, ваши задачи даже не начались. Отмена просто удалит его из пула. Если задача уже запущена, вы можете указать флаг потока isInterrupted() или дескриптор InterruptedException, чтобы обнаружить вызов cancel().