Как вызвать какой-либо метод блокировки с таймаутом в Java?
есть ли стандартный хороший способ вызвать метод блокировки с таймаутом в Java? Я хочу быть в состоянии сделать:
// call something.blockingMethod();
// if it hasn't come back within 2 seconds, forget it
если это имеет смысл.
спасибо.
10 ответов:
вы можете использовать исполнителя:
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
.посмотреть документация подробнее.
вы можете обернуть вызов в
FutureTask
и использовать тайм-аут версии get ().см.http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/FutureTask.html
существует также AspectJ решение для этого с jcabi-аспекты библиотека.
@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 }
он не может быть более кратким, но вы должны зависеть от AspectJ и вводить его в свой жизненный цикл сборки, конечно.
есть статья, объясняющая это дальше:Ограничить Время Выполнения Метода Java
см. также Guava TimeLimiter который использует исполнителя за кулисами.
Thread thread = new Thread(new Runnable() { public void run() { something.blockingMethod(); } }); thread.start(); thread.join(2000); if (thread.isAlive()) { thread.stop(); }
обратите внимание, что stop устарел, лучшей альтернативой является установка некоторого волатильного логического флага, внутри blockingMethod () проверьте его и выйдите, например:
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)"); } } }
попробуйте это. Более простое решение. Гарантирует, что если блок не был выполнен в течение срока. процесс завершится и вызовет исключение.
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 }
предположим
blockingMethod
просто поспи немного Миллис:public void blockingMethod(Object input) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }
мое решение-использовать
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(); } } }
так что вы можете заменить
something.blockingMethod(input)
до
something.blockingMethod(input, 2000)
надеюсь, что это помогает.
вам понадобится автоматический выключатель реализация, как тот, который присутствует в failsafe проект на GitHub.
Я даю вам здесь полный код. Вместо метода, который я вызываю, вы можете использовать свой метод.
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 } }
}
это действительно здорово, что люди пытаются реализовать это во многих отношениях. Но правда в том, что это невозможно.
большинство разработчиков попытались бы поместить блокирующий вызов в другой поток и иметь будущее или какой-то таймер. Но в Java нет способа остановить поток извне, не говоря уже о нескольких очень конкретных случаях, таких как поток.сон () и замок.lockInterruptibly() методы, которые явно обрабатывать прерывания потока.
Так что на самом деле у вас есть только 3 общих опции:
поместите свой блокирующий вызов на Новый Поток, и если время истекает, вы просто двигаетесь дальше, оставляя этот поток висящим. В этом случае вы должны убедиться, что поток является потоком демона. Таким образом, поток не остановит ваше приложение от завершения.
используйте неблокирующие API Java. Поэтому для сети, например, используйте NIO2 и используйте неблокирующие методы. Для чтения с консоли используйте сканер.hasNext() перед блокированием так далее.
Если ваш блокирующий вызов не является IO, но ваша логика, то вы можете повторно проверить
Thread.isInterrupted()
чтобы проверить, был ли он прерван извне, и иметь другой вызов потокаthread.interrupt()
на блокирующем потокеэтот курс о параллелизме https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
действительно проходит через эти основы, Если вы действительно хотите понять, как это работает в Java. Это на самом деле говорит о тех конкретных ограничениях и сценариях, и как идти о них в одной из лекций.
Я лично стараюсь программировать без использования блокировки вызовов как можно больше. Есть такие инструменты, как Vert.x например, что делает его очень легким и эффективным для выполнения операций ввода-вывода и без операций ввода-вывода асинхронно и не блокирующим способом.
надеюсь, это поможет