如何在 Java 中使用超时调用一些阻塞方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1164301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I call some blocking method with a timeout in Java?
提问by jjujuma
Is there a standard nice way to call a blocking method with a timeout in Java? I want to be able to do:
在 Java 中是否有一种标准的好方法来调用具有超时的阻塞方法?我希望能够做到:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
if that makes sense.
如果这是有道理的。
Thanks.
谢谢。
采纳答案by skaffman
You could use an Executor:
您可以使用执行器:
ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() {
public Object call() {
return something.blockingMethod();
}
};
Future<Object> future = executor.submit(task);
try {
Object result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException ex) {
// handle the timeout
} catch (InterruptedException e) {
// handle the interrupts
} catch (ExecutionException e) {
// handle other exceptions
} finally {
future.cancel(true); // may or may not desire this
}
If the future.get
doesn't return in 5 seconds, it throws a TimeoutException
. The timeout can be configured in seconds, minutes, milliseconds or any unit available as a constant in TimeUnit
.
如果future.get
5 秒内没有返回,它会抛出一个TimeoutException
. 超时可以以秒、分钟、毫秒或任何可用的单位配置为TimeUnit
.
See the JavaDocfor more detail.
有关更多详细信息,请参阅JavaDoc。
回答by Colin Goudie
You could wrap the call in a FutureTask
and use the timeout version of get().
您可以将调用包装在 a 中FutureTask
并使用 get() 的超时版本。
See http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
见http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
回答by jnr
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
Note, that stop is deprecated, better alternative is to set some volatile boolean flag, inside blockingMethod() check it and exit, like this:
请注意,不推荐使用 stop ,更好的选择是设置一些 volatile 布尔标志,在 blocksMethod() 内部检查它并退出,如下所示:
import org.junit.*;
import java.util.*;
import junit.framework.TestCase;
public class ThreadTest extends TestCase {
static class Something implements Runnable {
private volatile boolean stopRequested;
private final int steps;
private final long waitPerStep;
public Something(int steps, long waitPerStep) {
this.steps = steps;
this.waitPerStep = waitPerStep;
}
@Override
public void run() {
blockingMethod();
}
public void blockingMethod() {
try {
for (int i = 0; i < steps && !stopRequested; i++) {
doALittleBit();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void doALittleBit() throws InterruptedException {
Thread.sleep(waitPerStep);
}
public void setStopRequested(boolean stopRequested) {
this.stopRequested = stopRequested;
}
}
@Test
public void test() throws InterruptedException {
final Something somethingRunnable = new Something(5, 1000);
Thread thread = new Thread(somethingRunnable);
thread.start();
thread.join(2000);
if (thread.isAlive()) {
somethingRunnable.setStopRequested(true);
thread.join(2000);
assertFalse(thread.isAlive());
} else {
fail("Exptected to be alive (5 * 1000 > 2000)");
}
}
}
回答by Federico
See also Guava's TimeLimiterwhich uses an Executor behind the scenes.
另请参阅 Guava 的TimeLimiter,它在幕后使用了 Executor。
回答by Euporie
Assume blockingMethod
just sleep for some millis:
假设blockingMethod
只睡了几毫秒:
public void blockingMethod(Object input) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
My solution is to use wait()
and synchronized
like this:
我的解决方案是使用wait()
并synchronized
像这样:
public void blockingMethod(final Object input, long millis) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
blockingMethod(input);
synchronized (lock) {
lock.notify();
}
}
}).start();
synchronized (lock) {
try {
// Wait for specific millis and release the lock.
// If blockingMethod is done during waiting time, it will wake
// me up and give me the lock, and I will finish directly.
// Otherwise, when the waiting time is over and the
// blockingMethod is still
// running, I will reacquire the lock and finish.
lock.wait(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
So u can replace
所以你可以更换
something.blockingMethod(input)
something.blockingMethod(input)
to
到
something.blockingMethod(input, 2000)
something.blockingMethod(input, 2000)
Hope it helps.
希望能帮助到你。
回答by gvlasov
There is also an AspectJ solution for that with jcabi-aspectslibrary.
还有一个带有jcabi-aspects库的 AspectJ 解决方案。
@Timeable(limit = 30, unit = TimeUnit.MINUTES)
public Soup cookSoup() {
// Cook soup, but for no more than 30 minutes (throw and exception if it takes any longer
}
It can't get more succinct, but you have to depend on AspectJ and introduce it in your build lifecycle, of course.
再简洁不过了,当然,您必须依赖 AspectJ 并在构建生命周期中引入它。
There is an article explaining it further: Limit Java Method Execution Time
有一篇文章进一步解释了它:限制 Java 方法执行时间
回答by Marco Montel
You need a circuit breakerimplementation like the one present in the failsafeproject on GitHub.
回答by Niroshan Abeywickrama
Try this. More simple solution. Guarantees that if block didn't execute within the time limit. the process will terminate and throws an exception.
尝试这个。更简单的解决方案。保证 if 块没有在时限内执行。该进程将终止并抛出异常。
public class TimeoutBlock {
private final long timeoutMilliSeconds;
private long timeoutInteval=100;
public TimeoutBlock(long timeoutMilliSeconds){
this.timeoutMilliSeconds=timeoutMilliSeconds;
}
public void addBlock(Runnable runnable) throws Throwable{
long collectIntervals=0;
Thread timeoutWorker=new Thread(runnable);
timeoutWorker.start();
do{
if(collectIntervals>=this.timeoutMilliSeconds){
timeoutWorker.stop();
throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
}
collectIntervals+=timeoutInteval;
Thread.sleep(timeoutInteval);
}while(timeoutWorker.isAlive());
System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
}
/**
* @return the timeoutInteval
*/
public long getTimeoutInteval() {
return timeoutInteval;
}
/**
* @param timeoutInteval the timeoutInteval to set
*/
public void setTimeoutInteval(long timeoutInteval) {
this.timeoutInteval = timeoutInteval;
}
}
example :
例子 :
try {
TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
Runnable block=new Runnable() {
@Override
public void run() {
//TO DO write block of code
}
};
timeoutBlock.addBlock(block);// execute the runnable block
} catch (Throwable e) {
//catch the exception here . Which is block didn't execute within the time limit
}
回答by VickyCool
I'm giving you here the complete code. In place of the method I'm calling, you can use your method:
我在这里给你完整的代码。代替我正在调用的方法,您可以使用您的方法:
public class NewTimeout {
public String simpleMethod() {
return "simple method";
}
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Callable<Object> task = new Callable<Object>() {
public Object call() throws InterruptedException {
Thread.sleep(1100);
return new NewTimeout().simpleMethod();
}
};
Future<Object> future = executor.submit(task);
try {
Object result = future.get(1, TimeUnit.SECONDS);
System.out.println(result);
} catch (TimeoutException ex) {
System.out.println("Timeout............Timeout...........");
} catch (InterruptedException e) {
// handle the interrupts
} catch (ExecutionException e) {
// handle other exceptions
} finally {
executor.shutdown(); // may or may not desire this
}
}
}
回答by Michael P
It's really great that people try to implement this in so many ways. But the truth is, there is NO way.
人们尝试以多种方式实现这一点真的很棒。但事实是,没有办法。
Most developers would try to put the blocking call in a different thread and have a future or some timer. BUT there is no way in Java to stop a thread externally, let alone a few very specific cases like the Thread.sleep() and Lock.lockInterruptibly() methods that explicitly handle thread interruption.
大多数开发人员会尝试将阻塞调用放在不同的线程中,并有一个 future 或一些计时器。但是在 Java 中没有办法从外部停止线程,更不用说一些非常具体的情况了,比如 Thread.sleep() 和 Lock.lockInterruptably() 方法,它们明确地处理线程中断。
So really you have only 3 generic options:
所以实际上你只有 3 个通用选项:
Put your blocking call on a new thread and if the time expires you just move on, leaving that thread hanging. In that case you should make sure the thread is set to be a Daemon thread. This way the thread will not stop your application from terminating.
Use non blocking Java APIs. So for network for example, use NIO2 and use the non blocking methods. For reading from the console use Scanner.hasNext() before blocking etc.
If your blocking call is not an IO, but your logic, then you can repeatedly check for
Thread.isInterrupted()
to check if it was interrupted externally, and have another thread callthread.interrupt()
on the blocking thread
将您的阻塞调用放在一个新线程上,如果时间到期,您只需继续前进,让该线程挂起。在这种情况下,您应该确保将线程设置为守护线程。这样线程就不会阻止您的应用程序终止。
使用非阻塞 Java API。因此,对于网络,使用 NIO2 并使用非阻塞方法。要从控制台读取,请在阻塞等之前使用 Scanner.hasNext()。
如果您的阻塞调用不是 IO,而是您的逻辑,那么您可以反复检查
Thread.isInterrupted()
以检查它是否被外部中断,并thread.interrupt()
在阻塞线程上进行另一个线程调用
This course about concurrency https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
really walks through those fundamentals if you really want to understand how it works in Java. It actually talks about those specific limitations and scenarios, and how to go about them in one of the lectures.
如果您真的想了解它在 Java 中的工作原理,那么您将真正了解这些基础知识。它实际上讨论了那些特定的限制和场景,以及如何在其中一堂课中讨论它们。
I personally try to program without using blocking calls as much as possible. There are toolkits like Vert.x for example that make it really easy and performant to do IO and no IO operations asynchronously and in a non blocking way.
我个人尝试在编程时尽可能不使用阻塞调用。例如,有像 Vert.x 这样的工具包,可以非常轻松和高效地执行 IO,并且没有异步和非阻塞方式的 IO 操作。
I hope it helps
我希望它有帮助