1

I have two APIs: one starts the thread, and another stops the thread. I'm successfully able to start a thread by calling /start API, but I'm unable to stop already running thread by calling /stop API. Seems like Executor#stop() does nothing.

My RestController:

@Autowired
private Executor executor;

@RequestMapping(path = "/start", method = GET)
public ResponseEntity<HttpStatus> startLongTask() {
    executor.start();
    return ResponseEntity.ok(HttpStatus.OK);
}

@RequestMapping(path = "/stop", method = GET)
public ResponseEntity<HttpStatus> stopLongTask() {
    executor.stop();
    return ResponseEntity.ok(HttpStatus.OK);
}

My Executor:

@Component
public class Executor {

    @Value("${threads.number}")
    private int threadsNumber;

    private ExecutorService executorService;

    @Autowired
    private OtherService otherService;

    @PostConstruct
    private void init() {
        executorService = Executors.newFixedThreadPool(threadsNumber);
        executorService = Executors.newScheduledThreadPool(threadsNumber);
    }

    /**
     * Start.
     */
    public void start() {
        executorService.submit(() -> otherService.methodImExecuting());
    }

    /**
     * Stop.
     */
    @PreDestroy
    publicvoid stop() {
        executorService.shutdownNow();
        try {
            if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
        }
    }
}

Here's the methodImExecuting:

@Component
public class OtherService {

    public void methodImExecuting() {
        List<SomeObject> dataList = repository.getDataThatNeedsToBeFilled();
        for (SomeObject someObject : dataList) {
            gatewayService.sendDataToOtherResourceViaHttp(someObject);
        }
    }
}
htshame
  • 6,599
  • 5
  • 36
  • 56

2 Answers2

2

Short answer: You can not stop a running thread which does not cooperate. There's a deprecated destroy() method for threads, but this will lead to a "bad" state of your VM.

The only possibility to end the Thread clean is to interrupt it. But to check for interruption is the task of the thread itself.

So your methodImExcecuting sould look like:

void methodImExecuting() throws InterruptedException {
    // it depends on your implementation, I assume here that you iterate 
    // over a collection for example
    int loopCount = 0;
    for (Foo foo : foos) {
        ++loopCount;
        if (loopCount % 100 == 0) {
            if (Thread.interrupted())
                throw new InterruptedException();
        }
        ...
    }

It depends on your implementation how often you have to look if your thread was interrupted. But it's a fact that the call of executorService.shutdownNow(); will only set the interrupted flag of all threads currently running in the executorService. To really interrupt the thread, the thread must itself check if the interrupted flag is set and then throw an InterruptedException

Torsten Fehre
  • 567
  • 2
  • 7
1

Your running threads have to react to the interrupt signal

Thread.currentThread().isInterrupted()

Otherwise the sending of the interrupt signal has no effect.

Here you can find a good explanation: Difference between shutdown and shutdownNow of Executor Service

tomas
  • 830
  • 3
  • 8
  • 25