简体   繁体   English

如何正常停止正在运行阻塞操作的线程?

[英]How to gracefully stop a thread that is running a blocking operation?

I have a blocking operation running in a thread that I need to stop: 我有一个阻塞操作在我需要停止的线程中运行:

Future serverFuture = Executors.newCachedThreadPool().submit(() -> {
    var serverSocket = new ServerSocket(Config.SERVER_PORT);

    serverSocket.accept(); // <-- blocking
});

serverFuture.cancel(true);

Normally, I'd implement a loop that would keep checking whether the thread is interrupted and stop it eventually. 通常,我会实现一个循环来继续检查线程是否被中断并最终停止它。 However, since the thread is blocked in the serverSocket.accept() method, this is not possible. 但是,由于线程在serverSocket.accept()方法中被阻塞,因此这是不可能的。

What is the usual way to overcome this problem? 解决这个问题的常用方法是什么?

How to gracefully stop a thread that is running a blocking operation? 如何正常停止正在运行阻塞操作的线程?

In general, you can't. 一般来说,你不能。 But there are things you can do about some specific operations. 但是你可以对某些特定的操作做些事情。 For example, you can try interrupting the thread. 例如,您可以尝试中断线程。 In a case such as your example, where you don't have direct access to the Thread in which the task is running, you can use Future.cancel() : 在您的示例中,您无法直接访问运行任务的Thread ,您可以使用Future.cancel()

serverFuture.cancel(true);

But blocking operations do not necessarily get interrupted when their threads do. 但是阻塞操作不一定会在线程执行时中断。 Those that do are generally documented to throw InterruptedException , and that is not among the exceptions ServerSocket.accept() is declared to throw. 那些做的事情通常被记录为抛出InterruptedException ,并且这不是ServerSocket.accept()被声明抛出的异常。 For that method in particular, your best bet probably involves using ServerSocket.setSoTimeout() to put a suitable upper bound on the amount of time that accept() will block. 特别是对于该方法,最好的选择可能是使用ServerSocket.setSoTimeout()accept()阻塞的时间量上设置合适的上限。 Using a relatively short timeout and performing the accept() in a loop would allow you to check periodically whether to abort the task. 使用相对较短的超时并在循环中执行accept()将允许您定期检查是否中止任务。

Overall, then, you need to choose a strategy appropriate to the work you are trying to cancel, and you may need to expend some design effort to make that task cleanly interruptible. 总的来说,您需要选择适合您要取消的工作的策略,并且您可能需要花费一些设计工作来使该任务完全可以中断。

For this case, You can use close method of ServerSocket to interrupt accept method. 对于这种情况,您可以使用ServerSocket的close方法来中断accept方法。 This will throw SocketException on the thread that is blocked on the accept call. 这将在接受调用上阻塞的线程上抛出SocketException。 (To be able to use this, you should make your server socket object accessible where you want to cancel.) (为了能够使用它,您应该使您的服务器套接字对象可以在您想要取消的位置访问。)

In general most of blocking api's has a timeout parameter that can be specified for the blocking call. 通常,大多数阻塞api都有一个可以为阻塞调用指定的超时参数。 Or they can be interrupted with Thread.interrupt method called for the executing thread. 或者可以使用为执行线程调用的Thread.interrupt方法中断它们。

You need to set timeout on the server socket before calling accept() [1]: 在调用accept() [1]之前,需要在服务器套接字上设置超时:

serverSocket.setSoTimeout(100);

Then if it failed to accept any connection within the set timeout, accept() will throw SocketTimeoutException which you can use to decide if you want to continue/stop accepting connections by either breaking from the loop or calling accept() again 然后,如果它在设置超时内未能接受任何连接,则accept()将抛出SocketTimeoutException ,您可以使用它来决定是否要通过断开循环或再次调用accept()来继续/停止接受连接

[1] https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html#setSoTimeout(int) [1] https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html#setSoTimeout(int)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM