有没有一种标准的好方法在Java中调用带有超时的阻塞方法?我想要这样做:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
如果有意义的话。
谢谢。
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
如果有意义的话。
谢谢。
你可以使用 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
}
如果future.get
在5秒内没有返回,它会抛出TimeoutException
异常。超时时间可以配置为秒、分钟、毫秒或在TimeUnit
中可用的任何单位常量。BlockingMethodCallable
的新类,其构造函数接受您想要传递给blockingMethod()
的参数,并将它们作为成员变量存储(可能是final)。然后在call()
内部将这些参数传递给blockMethod()
。 - Vite Falconfuture.cancel(true)
- 类型 Future<Object> 中的方法 cancel(boolean) 不适用于参数 ()。 - Noam ManosFutureTask
中,并使用get()的超时版本。人们尝试以许多方式实现这一点,这真的很棒。但事实是,没有办法。
大多数开发人员会尝试将阻塞调用放在不同的线程中,并设置一个future或某个计时器。但是,在Java中没有外部停止线程的方法,更不用说一些非常特定的情况,例如Thread.sleep()和Lock.lockInterruptibly()方法,它们明确处理线程中断。
因此,您只有三个通用选项:
将阻塞调用放在新线程中,如果时间到期,您只需继续运行,留下该线程。在这种情况下,您应确保线程被设置为守护线程。这样,该线程将不会阻止应用程序终止。
使用非阻塞Java API。因此,对于网络,使用NIO2并使用非阻塞方法。对于从控制台读取,请在阻塞之前使用Scanner.hasNext()等。
如果您的阻塞调用不是IO,而是逻辑,则可以重复检查Thread.isInterrupted()以检查是否已被外部中断,并使另一个线程在阻塞线程上调用thread.interrupt()
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
}
}
}
此外,还有一个AspectJ解决方案,使用jcabi-aspects库。
@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技术的翻译内容,需要使用AspectJ来限制Java方法的执行时间。你需要在构建生命周期中引入AspectJ。以下是进一步解释的文章:Limit Java Method Execution Time
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;
}
}
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
}
Thread thread = new Thread(new Runnable() {
public void run() {
something.blockingMethod();
}
});
thread.start();
thread.join(2000);
if (thread.isAlive()) {
thread.stop();
}
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)");
}
}
}
在阻塞队列的特殊情况下:
通用的java.util.concurrent.SynchronousQueue
具有带有超时参数的poll
方法。