Java:在特定代码块上设置超时?
是否有可能在某些代码块运行时间超过可接受的时间后强制 Java 抛出异常?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
是否有可能在某些代码块运行时间超过可接受的时间后强制 Java 抛出异常?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(13)
这是我所知道的最简单的方法:
或者,您可以创建一个 TimeLimitedCodeBlock 类来包装此功能,然后您可以在任何需要的地方使用它,如下所示:
Here's the simplest way that I know of to do this:
Alternatively, you can create a TimeLimitedCodeBlock class to wrap this functionality, and then you can use it wherever you need it as follows:
我将其他一些答案编译成一个实用方法:
使用此实用方法的示例代码:
在我的计算机上运行示例代码的输出:
I compiled some of the other answers into a single utility method:
Sample code making use of this utility method:
Output from running the sample code on my machine:
是的,但强制另一个线程中断随机代码行通常是一个非常糟糕的主意。仅当您打算关闭该进程时才需要执行此操作。
您可以做的是在一段时间后使用 Thread.interrupt() 来执行任务。但是,除非代码对此进行检查,否则它将无法工作。 ExecutorService 可以通过
Future.cancel(true)
使这一点变得更容易。它可以更好地让代码自行计时并在需要时停止。
Yes, but its generally a very bad idea to force another thread to interrupt on a random line of code. You would only do this if you intend to shutdown the process.
What you can do is to use
Thread.interrupt()
for a task after a certain amount of time. However, unless the code checks for this it won't work. An ExecutorService can make this easier withFuture.cancel(true)
Its much better for the code to time itself and stop when it needs to.
如果是测试代码你想计时,那么你可以使用
time
属性:如果是生产代码,没有简单的机制,你使用哪种解决方案取决于你是否可以改变代码是否定时。
如果您可以更改正在计时的代码,那么一个简单的方法是让您的计时代码记住它的开始时间,并定期记住当前时间。例如,
如果代码本身无法检查超时,您可以在另一个线程上执行代码,并等待完成或超时。
If it is test code you want to time, then you can use the
time
attribute:If it is production code, there is no simple mechanism, and which solution you use depends upon whether you can alter the code to be timed or not.
If you can change the code being timed, then a simple approach is is to have your timed code remember it's start time, and periodically the current time against this. E.g.
If the code itself cannot check for timeout, you can execute the code on another thread, and wait for completion, or timeout.
我可以建议两个选择。
在该方法中,假设它正在循环并且不等待外部事件,请添加一个本地字段并测试每次循环的时间。
在线程中运行该方法,并让调用者数到 10 秒。
这种方法的缺点是 method() 不能直接返回值,它必须更新实例字段才能返回其值。
I can suggest two options.
Within the method, assuming it is looping and not waiting for an external event, add a local field and test the time each time around the loop.
Run the method in a thread, and have the caller count to 10 seconds.
The drawback to this approach is that method() cannot return a value directly, it must update an instance field to return its value.
编辑:Peter Lawrey 是完全正确的:它并不像中断线程(我最初的建议)和 Executors & 那样简单。 Callable 非常有用......
一旦达到超时,您可以在 Callable 上设置一个变量,而不是中断线程。可调用程序应在任务执行的适当时刻检查此变量,以了解何时停止。
Callables 返回 Futures,当您尝试“获取”未来的结果时,您可以使用它指定超时。像这样的东西:
请参阅 Future.get、Executors 和 Callable ...
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get-long-java.util.concurrent.TimeUnit-
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29
EDIT: Peter Lawrey is completely right: it's not as simple as interrupting a thread (my original suggestion), and Executors & Callables are very useful ...
Rather than interrupting threads, you could set a variable on the Callable once the timeout is reached. The callable should check this variable at appropriate points in task execution, to know when to stop.
Callables return Futures, with which you can specify a timeout when you try to 'get' the future's result. Something like this:
See Future.get, Executors, and Callable ...
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get-long-java.util.concurrent.TimeUnit-
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29
我创建了一个非常简单的解决方案,没有使用任何框架或 API。这看起来更优雅且更容易理解。该类称为 TimeoutBlock。
示例:
当我必须连接到 FTP 帐户时,这对我非常有用。然后下载并上传东西。有时 FTP 连接会挂起或完全中断。这导致整个系统瘫痪。我需要一种方法来检测它并防止它发生。所以我创建了这个并使用它。效果很好。
I created a very simple solution without using any frameworks or APIs. This looks more elegant and understandable. The class is called TimeoutBlock.
example :
This was so much useful for me when i had to connect to a FTP account. Then download and upload stuff. sometimes FTP connection hangs or totally breaks. This caused whole system to go down. and i needed a way to detect it and prevent it from happening . So i created this and used it. Works pretty well.
我遇到了类似的问题,我的任务是在特定的超时时间内将消息推送到 SQS。我使用了通过另一个线程执行它并通过指定超时来等待其未来对象的简单逻辑。如果超时,这会给我一个 TIMEOUT 异常。
但在某些情况下,您无法阻止另一个线程执行的代码,并且在这种情况下您会得到真正的负面结果。
例如 - 就我而言,我的请求到达了 SQS,在推送消息时,我的代码逻辑遇到了指定的超时。现在实际上我的消息已被推送到队列中,但我的主线程认为它由于超时异常而失败。
这是一类可以避免而不是解决的问题。就像我的例子一样,我通过提供一个超时来避免它,这在几乎所有情况下都足够了。
如果你想要中断的代码在你的应用程序中,而不是像 API 调用那样的东西,那么你可以简单地使用
但是请记住,java 文档说它确实保证执行将被阻止。
“尝试取消执行此任务。如果任务已完成、已被取消或由于其他原因无法取消,则此尝试将失败。如果成功,且此任务尚未开始当调用 cancel 时,该任务永远不应该运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该中断执行该任务的线程以尝试停止该任务。”
I faced a similar kind of issue where my task was to push a message to SQS within a particular timeout. I used the trivial logic of executing it via another thread and waiting on its future object by specifying the timeout. This would give me a TIMEOUT exception in case of timeouts.
But there are cases where you can't stop the code being executed by another thread and you get true negatives in that case.
For example - In my case, my request reached SQS and while the message was being pushed, my code logic encountered the specified timeout. Now in reality my message was pushed into the Queue but my main thread assumed it to be failed because of the TIMEOUT exception.
This is a type of problem which can be avoided rather than being solved. Like in my case I avoided it by providing a timeout which would suffice in nearly all of the cases.
If the code you want to interrupt is within you application and is not something like an API call then you can simply use
However do remember that java docs says that it does guarantee that the execution will be blocked.
"Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled,or could not be cancelled for some other reason. If successful,and this task has not started when cancel is called,this task should never run. If the task has already started,then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted inan attempt to stop the task."
如果你想要一个 CompletableFuture 方式,你可以有一个类似的方法,
如果你使用 spring,你可以用 @Retryable 注释该方法,以便在抛出异常时重试该方法三次。
If you want a CompletableFuture way you could have a method like
If you're using spring, you could annotate the method with a
@Retryable
so that it retries the method three times if an exception is thrown.不要将任务放在新线程中并将计时器放在主线程中,而是将计时器放在新线程中并将任务放在主线程中:
Instead of having the task in the new thread and the timer in the main thread, have the timer in the new thread and the task in the main thread:
有一种巧妙的方法可以做到这一点。
设置一些布尔字段来指示工作是否完成。然后在代码块之前设置一个计时器,以便在超时后运行一段代码。计时器将检查代码块是否已完成执行,如果没有,则抛出异常。否则它什么也做不了。
当然,代码块的末尾应该将该字段设置为 true 以指示工作已完成。
There is a hacky way to do it.
Set some boolean field to indicate whether the work was completed. Then before the block of code, set a timer to run a piece of code after your timeout. The timer will check if the block of code had finished executing, and if not, throw an exception. Otherwise it will do nothing.
The end of the block of code should, of course, set the field to true to indicate the work was done.
有一个非常简单的选项,但还没有人提到:
如果运行代码块的线程未能在超时内完成,它将被中断,并且可以抛出您想要的任何异常。
可以编写简单地忽略中断并继续执行的代码。如果您正在处理这个问题无法修复它,那么可以使用thread.stop(),但这可能会破坏您所依赖的任何同步机制。请参阅其 弃用通知。
您还可以从线程捕获异常:
There's a very simple option that nobody's mentioned yet:
If the thread running your code block fails to complete within the timeout, it is interrupted and whatever exception you want can be thrown.
It is possible to write code that will simply ignore the interruption and carry on. If you're dealing with this can cannot fix it then there is
thread.stop()
, but that can break any synchronisation mechanisms that you are relying on. See its deprecation notice.You can also capture exceptions from the thread:
我也遇到了这个问题,我的日志打印出“意外的流结束”和“无法从池中获取资源”,
我将brpop的超时设置为30s,redis设置为31s,mysql数据库连接池设置为300s。目前日志上还没有打印这个错误,但是不知道以后会不会报这个错误。不知道对我写入数据库有没有不好的影响
I had this problem too, my logs print out with ‘’Unexpected end of stream‘’.and ‘’Could not get a resource from the pool‘’,
I set the timeout of brpop to 30s, redis to 31s, and mysql database connection pool to 300s. For now, this error is not printed on the log, but I don't know if this error will be reported in the future.I don't know if it has a bad effect on my writing to the database