【问题标题】:Stop thread that's already running停止已经在运行的线程
【发布时间】:2018-09-13 13:24:06
【问题描述】:

我有两个 API:一个启动线程,另一个停止线程。我可以通过调用/start API 成功地启动一个线程,但是我无法通过调用/stop API 来停止已经运行的线程。好像Executor#stop() 什么都不做。

我的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);
}

我的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();
        }
    }
}

这是methodImExecuting

@Component
public class OtherService {

    public void methodImExecuting() {
        List<SomeObject> dataList = repository.getDataThatNeedsToBeFilled();
        for (SomeObject someObject : dataList) {
            gatewayService.sendDataToOtherResourceViaHttp(someObject);
        }
    }
}

【问题讨论】:

  • 方法methodImExecuting如何响应executorService尝试中断它很重要,因此我们需要查看该方法
  • @AndrewTobilko 我已将methodImExecuting 添加到问题中

标签: java multithreading spring-boot executorservice


【解决方案1】:

简答:你不能停止一个不合作的正在运行的线程。线程有一个已弃用的 destroy() 方法,但这会导致您的 VM 处于“不良”状态。

结束线程清理的唯一可能性是中断它。但是检查中断是线程本身的任务。

所以你的methodImExcecuting 看起来像:

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();
        }
        ...
    }

这取决于您的实现,如果您的线程被中断,您必须多久查看一次。但事实上executorService.shutdownNow();的调用只会设置executorService中当前运行的所有线程的interrupted标志。要真正中断线程,线程必须自己检查是否设置了interrupted 标志,然后抛出InterruptedException

【讨论】:

    【解决方案2】:

    您正在运行的线程必须对中断信号做出反应

    Thread.currentThread().isInterrupted()
    

    否则中断信号的发送无效。

    在这里你可以找到一个很好的解释: Difference between shutdown and shutdownNow of Executor Service

    【讨论】:

      最近更新 更多